求解单源最短路问题:Bellman-Ford算法(可判负权回路)详解 之 poj 3268 Silver Cow Party
/* 求解单源最短路问题:Bellman-Ford算法(可判负权回路) 注: 负权回路: 如果存在一个回路(首尾相同的路径),而且这个回路上所有权值之和是负数,那这就是一个负权回路。 f[i] := 从起点s出发到节点i的最短距离 故: f[i] = min(f[j] + dis[i][j]) 初始值: f[] = INF f[s] = 0; 自己当时遇到的疑惑,以及参考了相关资料获得的解答: (1)在实现时,最外侧循环的迭代次数为什么最大是|V|-1次?(|V|节点的个数) 答: 前提:在图中不存在负权回路 一共有n个节点,求1->n的最短距离,我们知道1->n的最短路径,最多有(n-1)条边。 (如果大于(n-1),说明有一个节点在最短路径上走了两次,即走了一个圈, 若圈的权为正:显然包含整个圈的路径必定不是最短路径; 若圈的权为负:最短路径不存在,因为到达节点的路径距离可以无限变小; 若圈的权为0: 去掉之后不影响最优解。 ) 在每次循环中,我们利用f[i] = min(f[j] + dis[i][j])来更新(s->i)的边,每 次循环必定会更新成功一条边(更新成功指的是:这条边的权值也是最终最短路径的权值), 那么需要(n-1)次循环才可以更新掉所有的边。 在《数据结构与编程实验--大学生程序设计课程与竞赛训练教材》(http://book.douban.com/subject/10537877/)上,
有说到: 很多时候,需要的迭代次数远小于|V|-1,所以可以考虑在每次循环中设置的update的标记, 如果没有update,则说明已获得最优解,跳出循环即可。 时间复杂度:O(V*E) (2)为什么如果f[i]>f[j]+dis[j][i],可以判定有负权回路? 答: 根本原因:求解最短路时 f[i] = min(f[j] + dis[i][j]) 在|V|-1此循环结束,即已获得最优解,如果在图中不存在负权回路,那么必然是f[i]<=f[j]+dis[j][i], 否则必然存在s->i的路径距离比f[i]更短,与已获得最优解矛盾。 代码: for (int i = 0; i < eg.size(); ++i) { if (f[eg[i].egto] > f[eg[i].egfrom] + eg[i].egfrom) { return false; // 出现负权回路 } return true; // 没有出现负权回路 } eg:
初始:
边更新后: 3 > 0 + 2 => 出现负权回路------------------------------------------------------- poj 3268 Silver Cow Party what is the longest amount of time a cow must spend walking to the party and back? 故此题需要解决两个问题: (1)所有节点到达节点X的最短距离;
对于此问题,可以反过来想:从X节点到达所有节点的最短距离,答案不变。
但是此时初始存储的有向边的方向需要调换
(原因:路是单向的,a->b反过来想a<-b,边的方向发生了改变,
边的权值不变,求解a->b的最短路也就是求a<-b的最短路
)
(2)节点X到达所有节点的最短距离。
按初始存储的有向边进行计算
答案:
max{ walkTo[i]+returnTo[i] }
*/
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstddef> 5 #include <iterator> 6 #include <algorithm> 7 #include <string> 8 #include <locale> 9 #include <cmath> 10 #include <vector> 11 #include <cstring> 12 #include <map> 13 #include <utility> 14 #include <queue> 15 #include <stack> 16 #include <set> 17 using namespace std; 18 const int INF = 0x3f3f3f3f; 19 const int MaxN = 205; 20 const int modPrime = 3046721; 21 22 struct Edge 23 { 24 int egfrom, egto, egcost; 25 }; 26 27 int N, M, X; 28 Edge eg[100010]; 29 int walkTo[1010]; // cow walk to the party 30 int returnTo[1010]; // cow return to her farm 31 32 void Solve() 33 { 34 bool update = true; 35 fill(walkTo, walkTo + 1010, INF); 36 walkTo[X] = 0; 37 for (int i = 0; i < N && update; ++i) 38 { 39 update = false; 40 for (int j = 0; j < M; ++j) 41 { 42 if ((walkTo[eg[j].egto] != INF) && (walkTo[eg[j].egfrom] > walkTo[eg[j].egto] + eg[j].egcost)) 43 { 44 walkTo[eg[j].egfrom] = walkTo[eg[j].egto] + eg[j].egcost; 45 update = true; 46 } 47 } 48 } 49 50 fill(returnTo, returnTo + 1010, INF); 51 returnTo[X] = 0; 52 update = true; 53 for (int i = 0; i < N && update; ++i) 54 { 55 update = false; 56 for (int j = 0; j < M; ++j) 57 { 58 if ((returnTo[eg[j].egfrom] != INF) && (returnTo[eg[j].egto] > returnTo[eg[j].egfrom] + eg[j].egcost)) 59 { 60 returnTo[eg[j].egto] = returnTo[eg[j].egfrom] + eg[j].egcost; 61 update = true; 62 } 63 } 64 } 65 66 int ans = 0; 67 for (int i = 1; i <= N; ++i) 68 { 69 ans = max(ans, walkTo[i] + returnTo[i]); 70 } 71 cout << ans << endl; 72 } 73 74 int main() 75 { 76 #ifdef HOME 77 freopen("in", "r", stdin); 78 //freopen("out", "w", stdout); 79 #endif 80 81 cin >> N >> M >> X; 82 for (int i = 0; i < M; ++i) 83 { 84 cin >> eg[i].egfrom >> eg[i].egto >> eg[i].egcost; 85 } 86 Solve(); 87 88 89 #ifdef HOME 90 cerr << "Time elapsed: " << clock() / CLOCKS_PER_SEC << " ms" << endl; 91 _CrtDumpMemoryLeaks(); 92 #endif 93 return 0; 94 }