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 }

 

    原文作者:算法小白
    原文地址: https://www.cnblogs.com/Inkblots/p/5452439.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞