最短路专题中的一道用到Bellman-Ford算法的题。
先简单描述题目的意思:
两个王国将开展一场星级战争,其中一个国家的防御系统被出卖,但是其中的信息有真有假。现在给出某些防御塔间的位置关系,判断是否有矛盾,有矛盾就是不可信,否则就是可信。其中有些是知道具体的相对位置,其余的知知道大概的相对方向,而且各个防御塔都是在南北向的一条直线上。
根据题意,就可以知道这是要判断是否有矛盾的信息,换句话说就是要判断是否有负权的回路。其中,较为简单而且经典的方法就是Bellman Ford的算法。
根据《算法导论》描述:
这个算法能在一般的情况(存在负权边的情况)下,解决单源最短路径问题。对于给定的带权有向图G=(V, E),其源点为s,加权函数为w:E→R,对该图运行Bellman-Ford算法后可以返回一个布尔值,表明图中是否存在一个从源点可达的权为负的回路。若存在这样的回路的话,算法说明该问题无解;若不存在这样的回路,算法将产生最短路径及其权值。
For i:=1 to |V|-1 do For 每条边(u,v)∈E do Relax(u,v,w); For每条边(u,v)∈E do If dis[u]+w<dis[v] Then Exit(False)
http://www.nocow.cn/index.php?title=Bellman-Ford%E7%AE%97%E6%B3%95&oldid=8177#.E6.94.B9.E8.BF.9B.E5.92.8C.E4.BC.98.E5.8C.96
这个算法的时间复杂度是O(VE),用于判断是否有负权回路时,它还能稍作优化,我的代码中已实现:
View Code
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #include<math.h>
5
6 #define Infp 0x7fffffff
7 #define Infn 0x80000000
8 #define DisCon 1000000000
9 #define MAX 999999999
10 #define MIN -999999999
11
12 #define Z(a) memset(a, 0, sizeof(a))
13 #define C(a, b) memcpy(a, b, sizeof(b))
14 #define Pi acos(-1)
15 #define FMAX (1E300)
16 #define FMIN (-1E300)
17 #define feq(a, b) (fabs((a)-(b))<1E-6)
18 #define flq(a, b) ((a)<(b)||feq(a, b))
19 #define RG 1005
20
21 struct Edge
22 {
23 int b;
24 int e;
25 int dis;
26 }e[200010];//简单的记录边的信息
27
28 int bell(int en, int pn)
29 {
30 int dis[pn+1];
31 int chg;
32
33 dis[0]=0;
34 for(int i=1; i<=pn; i++)
35 dis[i]=DisCon;//这是为了防止溢出才不用最大值
36 for(int i=0; i<pn; i++)
37 {
38 chg=0;
39 for(int j=0; j<en; j++)
40 if(dis[e[j].e]>dis[e[j].b]+e[j].dis)//松弛操作
41 {
42 dis[e[j].e]=dis[e[j].b]+e[j].dis;
43 chg=1;//如果最终趋于稳定,就是说肯定没有负权回路了
44 }
45 if(!chg)return 1;
46 /*************解释*****************************
47 结合《算法导论》的解释,如果从单源出发,而且图是无负权回路的,
48 那么到任何一个短点的最短路径最多只包含|V|-1条边,而上述代码最
49 外层的循环次数是|V|次,就是最多将构造含有|V|条边的最短路集合。
50 假设循环不断进行,一直有最短路被发现(也即一直有松弛的路径更新
51 ),直到i>|V|而退出,这说明这时构造出了一条最短路是有多于|V|条
52 边的,与初衷矛盾,所以这时说明了图是有负权回路的!
53 *********************************************/
54 }
55 return 0;
56 }
57
58 int main()
59 {
60 int N, M;
61
62 while(scanf("%d%d", &N, &M)!=EOF)
63 {
64 int en=0;
65 while(M--)
66 {
67 getchar();
68
69 char c;
70 scanf("%c", &c);
71 if(c=='P')//具体的信息,双向的边都要构造
72 {
73 int a, b, dis;
74
75 scanf("%d%d%d", &a, &b, &dis);
76 e[en].b=a;
77 e[en].e=b;
78 e[en++].dis=dis;
79 e[en].b=b;
80 e[en].e=a;
81 e[en++].dis=-dis;
82 }
83 else//模糊的信息,据说边长一定大于1,只构造单向的边
84 {
85 int a, b;
86 scanf("%d%d", &a, &b);
87 e[en].b=b;
88 e[en].e=a;
89 e[en++].dis=-1;
90 }
91 }
92
93 if(bell(en, N))printf("Reliable\n");
94 else printf("Unreliable\n");
95 }
96
97 return 0;
98 }
UPD:
看了下差分约束做,结果反而不能1y了。。- -
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstring> 5 6 using namespace std; 7 8 const int N = 1111; 9 const int M = 111111; 10 const int INF = 0x11111111; 11 struct Edge { 12 int u, v, c; 13 Edge() {} 14 Edge(int u, int v, int c) : u(u), v(v), c(c) {} 15 } edge[M << 1]; 16 int dis[N]; 17 18 bool bellman(int m) { 19 bool ok; 20 for (int i = 0; i < N; i++) dis[i] = INF; 21 for (int i = 0; i < N; i++) { 22 ok = true; 23 for (int j = 0; j < m; j++) { 24 if (dis[edge[j].v] > dis[edge[j].u] + edge[j].c) { 25 dis[edge[j].v] = dis[edge[j].u] + edge[j].c; 26 ok = false; 27 } 28 } 29 if (ok) return true; 30 } 31 return false; 32 } 33 34 int main() { 35 // freopen("in", "r", stdin); 36 int n, m; 37 int x, y, c; 38 char buf[3]; 39 while (~scanf("%d%d", &n, &m)) { 40 int cnt = 0; 41 for (int i = 0; i < m; i++) { 42 scanf("%s%d%d", buf, &x, &y); 43 if (buf[0] == 'P') { 44 scanf("%d", &c); 45 edge[cnt++] = Edge(x, y, c); 46 edge[cnt++] = Edge(y, x, -c); 47 } else edge[cnt++] = Edge(y, x, -1); 48 } 49 if (bellman(cnt)) puts("Reliable"); 50 else puts("Unreliable"); 51 } 52 return 0; 53 }
——written by Lyon