ACM/ICPC 之 最短路-Floyd+SPFA(BFS)+DP(ZOJ1232)
这是一道非常好的题目,融合了很多知识点。
ZOJ1232-Adventrue of Super Mario
这一题折磨我挺长时间的,不过最后做出来非常开心啊,哇咔咔咔
题意就不累述了,注释有写,难点在于状态转移方程的确立和SPFA的过程
1 //最短路:Floyd+SPFA(BFS)+DP 2 //Time:20Ms Memory:336K 3 //题目很好,数据较弱,网上部分代码有些问题却能够A掉 4 //题意:超级马里奥要从A+B处背着公主以最短路程到达1处,其中1-A是村庄,剩下的是城堡 5 // 有可使用K次可飞过L长的靴子(每次都以结点开始或结束),求最短路长 6 //首先需要得到任意两点之间的最短路-Floyd较为简便(10^5次操作也能接受) 7 //其次需要利用BFS从A+B处开始遍历并进行状态转移-BFS+DP 8 //构造状态:DP[i][k]:从i到A+B经过k次瞬移得到的最短路 9 //状态转移方程: 10 //能够从x瞬移到y:dp[x][k] = min(dp[x][k], dp[y][k - 1], dp[y][k] + d[x][y]) 11 //不能从x瞬移到y:dp[x][k] = min(dp[x][k], dp[y][k] + d[x][y]) 12 #include<iostream> 13 #include<cstring> 14 #include<cstdio> 15 #include<algorithm> 16 #include<queue> 17 #include<vector> 18 using namespace std; 19 20 #define INF 0x3f3f3f3f 21 #define MAX 105 22 23 vector<int> e[MAX]; //邻接表 24 int A, B, M, L, K; 25 int d[MAX][MAX]; //distance 26 bool fly[MAX][MAX]; //能否瞬移 27 int vis[MAX]; 28 int dp[MAX][12]; //dp[i][k]:从i到A+B经过k次瞬移得到的最短路 29 30 void floyd(int N) 31 { 32 for (int i = 1; i <= N; i++) 33 d[i][i] = 0; 34 for (int k = 1; k <= N; k++) 35 for (int i = 1; i <= N; i++) 36 for (int j = 1; j <= N; j++) 37 if (d[i][j] > d[i][k] + d[k][j]) 38 { 39 d[i][j] = d[i][k] + d[k][j]; 40 if (k <= A && d[i][j] <= L) 41 { 42 fly[i][j] = true; 43 e[i].push_back(j); 44 } 45 } 46 } 47 48 int main() 49 { 50 //freopen("in.txt", "r", stdin); 51 //freopen("out-2.txt", "w", stdout); 52 int T; 53 scanf("%d", &T); 54 while (T--) { 55 memset(d, INF, sizeof(d)); 56 memset(fly, false, sizeof(fly)); 57 memset(e, 0, sizeof(e)); 58 scanf("%d%d%d%d%d", &A, &B, &M, &L, &K); 59 while (M--) { 60 int u, v, w; 61 scanf("%d%d%d", &u, &v, &w); 62 d[v][u] = d[u][v] = w; 63 e[v].push_back(u); 64 e[u].push_back(v); 65 if (w <= L) fly[u][v] = fly[v][u] = true; 66 } 67 68 floyd(A + B); 69 //类似SPFA的过程(BFS) 70 memset(dp, INF, sizeof(dp)); 71 for (int i = 1; i <= K; i++) 72 dp[A + B][i] = 0; 73 for (int i = 1; i <= A + B; i++) 74 dp[i][0] = d[i][A + B]; 75 for (int k = 1; k <= K; k++) 76 { 77 memset(vis, false, sizeof(vis)); 78 queue<int> q; 79 q.push(A + B); //从A+B开始遍历 80 vis[A + B] = true; 81 while (!q.empty()) { 82 int cur = q.front(); 83 q.pop(); 84 for (int i = 0; i < e[cur].size(); i++) 85 { 86 int u = e[cur][i]; 87 int tmp = dp[u][k]; 88 //状态转移 89 if (fly[u][cur]) //可瞬移 90 dp[u][k] = min(dp[u][k], dp[cur][k - 1]); 91 dp[u][k] = min(dp[u][k], dp[cur][k] + d[cur][u]); 92 //需要转移状态的条件 - 没有访问过 or 最短路长变更 93 if (!vis[u] || tmp != dp[u][k]) 94 q.push(u); 95 vis[u] = true; 96 } 97 } 98 } 99 printf("%d\n", dp[1][K]); 100 } 101 return 0; 102 }
他坐在湖边,望向天空,她坐在对岸,盯着湖面