求解单源最短路问题: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 }

 

 
posted @ 2015-12-07 00:00  JmingS  阅读(419)  评论(0编辑  收藏  举报