ZOJ 1232 Adventure of Super Mario (Floyd + DP)
题意:有a个村庄,编号为1到a,有b个城堡,编号为a+1到a+b。现在超级玛丽在a+b处,他的家在1处。每条路是双向的,两端地点的编号以及路的长度都已给出。路的长度和通过所需时间相等。他有一双鞋子,可以使用k次,每次使用后最多可以跑过l的距离,且通过这段距离所需时间为0。使用鞋子时,必须从村庄或城堡开始,到村庄或者城堡结束。但是,城堡充满了陷阱,他如果中途遇见城堡,就必须停下来,且鞋子视为使用完了一次。问超级玛丽回家所需的最短时间。
思路:用floyd算法先处理出任意两点间的最短距离(不用鞋子时)。另用一个二维数组来维护两点之间是否允许用鞋子直接传送。当两点最短距离不大于l且中间不经过城堡时,数组值为真。
然后,进行dp。dp[i][k]表示从1到i用了k次鞋子时的最短时间(边是双向的,从1开始和从a+b开始是等价的)。
dp[i][k] = min(dp[j][k-1], dp[j][k])。当j到i不允许穿鞋子时,只有后面一项。最后dp[a+b][k]即为最终结果。
1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #define inf 0x3f3f3f3f 5 #define maxn 105 6 using namespace std; 7 int a, b, m, l, tk; 8 int d[maxn][maxn], ok[maxn][maxn], dp[maxn][12]; 9 void floyd() 10 { 11 for (int k = 1; k <= a + b; k++) 12 for (int i = 1; i <= a + b; i++) 13 for (int j = 1; j <= a + b; j++) 14 if (d[i][j] > d[i][k] + d[k][j]) 15 { 16 d[i][j] = d[i][k] + d[k][j]; 17 if (k <= a && d[i][j] <= l) ok[i][j] = ok[j][i] = 1; 18 } 19 } 20 void run_dp() 21 { 22 for (int i = 1; i <= a + b; i++) 23 dp[i][0] = d[1][i]; 24 for (int i = 0; i <= tk; i++) 25 dp[1][i] = 0; 26 for (int i = 2; i <= a + b; i++) 27 for (int k = 1; k <= tk; k++) 28 { 29 int tmin = inf; 30 for (int j = 1; j < i; j++) 31 { 32 if (ok[j][i]) 33 tmin = min(tmin, dp[j][k-1]); 34 tmin = min(tmin, dp[j][k] + d[j][i]); 35 } 36 dp[i][k] = tmin; 37 } 38 } 39 int main() 40 { 41 int t; 42 //freopen("data.in", "r", stdin); 43 scanf("%d", &t); 44 while (t--) 45 { 46 scanf("%d%d%d%d%d",&a,&b,&m,&l,&tk); 47 memset(d, 0x3f, sizeof(d)); 48 memset(ok, 0, sizeof(ok)); 49 while (m--) 50 { 51 int u, v, w; 52 scanf("%d%d%d",&u,&v,&w); 53 d[u][v] = d[v][u] = w; 54 if (w <= l) ok[u][v] = ok[v][u] = 1; 55 } 56 floyd(); 57 run_dp(); 58 printf("%d\n",dp[a+b][tk]); 59 } 60 return 0; 61 }