【原创】POJ 3259 Wormholes(Bellman-Ford) && 简介Bellman-Ford算法
【原创】
题目大意
John有N个农场,一共有M条边,在农场上出现了W个虫洞(W是一条边),其中M是双向普通边,W是单向虫洞边。John穿行于农场之间每经过一条边(S到E)的时间为+T,每经过虫洞会时间倒流,经过-T。问John会不会在某一刻看到以前的自己。这个题目即问的是,存不存在负权环。bollman_ford
先粘一下百度百科的话:
Bellman - ford算法是求含负权图的单源最短路径算法,效率很低,但代码很容易写。其原理为持续地进行松弛(原文是这么写的,为什么要叫松弛,争议很大),在每次松弛时把每条边都更新一下,若在n-1次松弛后还能更新,则说明图中有负环,因此无法得出结果,否则就完成。Bellman - ford算法有一个小优化:每次松弛先设一个标识flag,初值为FALSE,若有边更新则赋值为TRUE,最终如果还是FALSE则直接成功退出。Bellman-ford算法浪费了许多时间去做没有必要的松弛,而SPFA算法用队列进行了优化,效果十分显著,高效难以想象。SPFA还有SLF,LLL,滚动数组等优化。
这其他的不多说,先说一下松弛计算,其实就是更新距离嘛,这种思想在运筹学好像称之为松弛,我们知道,假如图中的边全部为正权边就一定会有最短路径,假如存在负权环,那么每走一圈,起点的权值就会比原来小,那么你走无数圈,你的权值就会无限减小。那么百度百科的意思就是,你先做标准次数的距离更新(理论上讲已经无法再更新了),假如你再一次更新距离,如果还能更新,就一定存在负权环咯。
1 /*题目大意
2 John有N个农场,一共有M条边,在农场上出现了W个虫洞(W是一条边),其中M是双向普通边
3 ,W是单向虫洞边。John穿行于农场之间每经过一条边(S到E)的时间为+T,每经过虫洞会时
4 间倒流,经过-T。问John会不会在某一刻看到以前的自己。
5 这个题目即问的是,存不存在负权环。bollman_ford
6 */
7
8 #include<cstdio>
9 #include<iostream>
10 #include<memory.h>
11 #define max 9999999
12 using namespace std;
13 int n,m,w,top;
14 typedef struct
15 {
16 int x,y,t;
17 } e;
18 e edge[6000];
19
20 bool bellman_ford(int en)//en = top-1,即边的数量
21 {
22 int dis[520],u,v,w;
23 for(int i=1;i<=n;i++) dis[i] = max;
24 dis[1] = 0;
25 for(int i=0;i<n-1;i++) //百科说:首先指出,图的任意一条最短路径既不能包含负权回路,也不会包含正权回路,因此它最多包含|v|-1条边。
26 {
27 //for(int k=1;k<=n;k++) printf("%d ",dis[k]);
28 for(int j = 1;j <= en;j++)
29 {
30 u = edge[j].x;
31 v = edge[j].y;
32 w = edge[j].t;
33 if(dis[u]<max && dis[u]+w < dis[v])
34 {
35 dis[v] = dis[u]+w;
36 }
37 }
38 }
39 for(int i=1;i<=en;i++)
40 {
41 u = edge[i].x;
42 v = edge[i].y;
43 w = edge[i].t;
44 if(dis[u]<max && dis[u]+w<dis[v])
45 {
46 return true;//极限更新n-1,我们再更新en次,如果还能再更新,那返回TRUE表示存在负权环
47 }
48 }
49 return false;//不能再更新了,返回FALSE
50 }
51 int main(){
52 int T;
53 scanf("%d", &T);
54 while(T--){
55 top = 1;
56 scanf("%d %d %d", &n, &m, &w);
57 for(int i=0; i<m; i++){
58 int a,b,c;
59 scanf("%d %d %d", &a, &b, &c);
60 edge[top].x = a;
61 edge[top].y = b;
62 edge[top].t = c;
63 top++;
64 edge[top].x = b; //这里一定要注意,普通的边是双向的,所以要重新反过来存一次
65 edge[top].y = a;
66 edge[top].t = c;
67 top++;
68 }
69 for(int i=0; i<w; i++){
70 int a,b,c;
71 scanf("%d %d %d", &a, &b, &c);
72 edge[top].x = a;
73 edge[top].y = b;
74 edge[top].t = -c; //虫洞是负的且是单向所以 取负 只用存一次
75 top++;
76 }
77 if(bellman_ford(top-1)) printf("YES\n");
78 else printf("NO\n");
79 }
80 }