POJ2686 Traveling by Stagecoach(状压DP)
题意:
有一个旅行家计划乘马车旅行。他所在的国家里共有m个城市,在城市之间有若干道路相连。从某个城市沿着某条道路到相邻的城市需要乘坐马车。而乘坐马车需要使用车票,每用一张车票只可以通过一条道路。每张车票上都记有马的匹数,从一个城市移动到另一个城市的所需时间等于城市之间道路的长度除以马的数量的结果。这位旅行家一共有n张车票,第i张车票上马的匹数是ti。一张车票只能使用一次,并且换乘所需要的时间可以忽略。求从城市a到城市b所需要的最短时间。如果无法到达城市b则输出”Impossible”。
分析:
一道经典的状态压缩DP,例如当前状态为“现在在城市V,此时还剩下的车票集合为S".从此状态出发,使用一张在S集合里面的车票i ,就可以转移到城市U".
dp[S][v]含义:旅行家还剩余的马车票组成集合S,并且已到达城市v的时候所花的时间总和。若目前已到达城市v,且马车票组成的集合为S,此时使用集合S中的第i张马车票到达城市u,那么状态转移过程可以表示为:
dp[S&~(1<<i)][u]=min{dp[S&~(1<<i)][u],dp[s][v]+d[s][v]/t[i]};
AC
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int maxn = 1 << 10; const int maxm = 31; const int INF = 1 << 29; int n, m, p, a, b; int t[maxm]; int d[maxm][maxm]; //图的邻接矩阵表示(-1表示没有边) double dp[maxn][maxm]; //dp[S][v] := 到达剩下的车票集合为S并且现在在城市v的状态所需要的最小花费 void so() { for (int i = 0; i < (1 << n); i++) fill(dp[i], dp[i] + m + 1, INF); //用足够大的值初始化 dp[(1 << n) - 1][a] = 0; double res = INF; for (int i = (1 << n) - 1; i >= 0; i--){//枚举票的状态 for (int u = 1; u <= m; u++){//起点u for (int j = 0; j < n; j++){//枚举每种票的情况 if (i & (1 << j)){//当前票的状态中有第j票 for (int v = 1; v <= m; v++){//枚举可以去到的城市 if (d[v][u]){ //使用车票i,从v移动到u dp[i & ~(1 << j)][v] = min(dp[i & ~(1 << j)][v], dp[i][u] + (double)d[u][v] / t[j]); } } } } } } for (int i = 0; i < (1 << n); i++) res = min(res, dp[i][b]); if (res == INF) //无法到达 printf("Impossible\n"); else printf("%.3f\n", res); } int main( ) { while(scanf("%d%d%d%d%d",&n,&m,&p,&a,&b)!=EOF) { if(n==0&&m==0) break; memset(d,0,sizeof(d)); for(int i=0 ; i<n ; i++) scanf("%d",&t[i]); while(p--) { int u,v,w; scanf("%d%d%d",&u,&v,&w); d[v][u]=d[u][v]=w; } so( ); } return 0; }