【洛谷 P3385】模板-负环(图论--spfa)
题目:有一个图有N个顶点,M条边。边用三个整数a b w表示,意思为a->b有一条权值为w的边(若w<0则为单向,否则双向)。共T组数据。对于每组数据,存在负环则输出一行"YE5"(不含引号),否则输出一行"N0"(不含引号)。
注意——坑爹的输出啊!!它不是平常的 YES 和 NO!!
解法:1.spfa_bfs,判断结点入队超过 n 次就出现负环。最差的情况是O(nm)。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<queue> 6 using namespace std; 7 8 const int N=200010; 9 struct edge{int x,y,d,next;}a[N*2]; 10 int last[N],vis[N],cnt[N],d[N]; 11 int len,n,m; 12 queue<int> q; 13 14 void ins(int x,int y,int d) 15 { 16 a[++len].x=x,a[len].y=y,a[len].d=d; 17 a[len].next=last[x],last[x]=len; 18 } 19 bool spfa() 20 { 21 while (!q.empty()) q.pop(); 22 memset(d,63,sizeof(d)); 23 memset(cnt,0,sizeof(cnt)); 24 memset(vis,0,sizeof(vis)); 25 d[1]=0,vis[1]=1,cnt[1]++; 26 q.push(1); 27 while (!q.empty()) 28 { 29 int x=q.front(); 30 q.pop(); vis[x]=0;//此处无cnt++ 31 for (int i=last[x];i!=-1;i=a[i].next) 32 { 33 int y=a[i].y; 34 if (d[x]+a[i].d<d[y]) 35 { 36 d[y]=d[x]+a[i].d; 37 if (!vis[y]) 38 { 39 q.push(y); 40 vis[y]=1, cnt[y]++; 41 if (cnt[y]>n) return true; 42 } 43 } 44 } 45 } 46 return false; 47 } 48 int main() 49 { 50 int T; 51 scanf("%d",&T); 52 while (T--) 53 { 54 scanf("%d%d",&n,&m); 55 int x,y,d; len=0; 56 memset(last,-1,sizeof(last)); 57 for (int i=1;i<=m;i++) 58 { 59 scanf("%d%d%d",&x,&y,&d); 60 ins(x,y,d); 61 if (d>=0) ins(y,x,d); 62 } 63 if (spfa()) printf("YE5\n"); 64 else printf("N0\n"); 65 } 66 return 0; 67 }
2.spfa_dfs,枚举起点,找最短路,若再一次访问到已经访问过的结点就出现负环。最差的情况也是O(nm)。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6 7 const int N=200010,INF=(int)1e9; 8 struct edge{int x,y,d,next;}a[N*2]; 9 int last[N],vis[N],dis[N]; 10 int len,n,m; 11 12 void ins(int x,int y,int d) 13 { 14 a[++len].x=x,a[len].y=y,a[len].d=d; 15 a[len].next=last[x],last[x]=len; 16 } 17 bool dfs(int x) 18 { 19 if (vis[x]) return true; 20 vis[x]=1; 21 for (int i=last[x];i!=-1;i=a[i].next) 22 { 23 int y=a[i].y; 24 //if (y==fa) continue;//有dis的判断 25 if (dis[x]+a[i].d<dis[y]) 26 { 27 dis[y]=dis[x]+a[i].d; 28 if (dfs(y)) return true; 29 } 30 } 31 vis[x]=0;//判断一个点是否在“同一”路径重复出现而已。 32 return false;// 33 } 34 int main() 35 { 36 int T; 37 scanf("%d",&T); 38 while (T--) 39 { 40 scanf("%d%d",&n,&m); 41 int x,y,d; len=0; 42 memset(last,-1,sizeof(last)); 43 for (int i=1;i<=m;i++) 44 { 45 scanf("%d%d%d",&x,&y,&d); 46 ins(x,y,d); 47 if (d>=0) ins(y,x,d); 48 } 49 for (int i=1;i<=n;i++) dis[i]=INF; 50 memset(vis,0,sizeof(vis)); 51 dis[1]=0;//起点不确定的! 52 if (dfs(1)) printf("YE5\n"); 53 else printf("N0\n"); 54 } 55 return 0; 56 }
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6 typedef long long LL; 7 8 const int N=200010,D=200010; 9 const LL INF=(LL)5e10; 10 int n,m,len; 11 bool cir; 12 int last[N],vis[N]; 13 LL dis[N]; 14 struct edge{int x,y,next;LL d;}a[2*N]; 15 16 void ins(int x,int y,LL d) 17 { 18 a[++len].x=x,a[len].y=y,a[len].d=d; 19 a[len].next=last[x],last[x]=len; 20 } 21 void dfs(int x) 22 { 23 if (vis[x]) {cir=true;return;} 24 vis[x]=1; 25 for (int i=last[x];i;i=a[i].next) 26 { 27 int y=a[i].y; 28 if (dis[x]+a[i].d<dis[y]) 29 { 30 dis[y]=dis[x]+a[i].d; 31 dfs(y); 32 if (cir) return; 33 } 34 } 35 vis[x]=0; 36 } 37 int main() 38 { 39 int T; 40 scanf("%d",&T); 41 while (T--) 42 { 43 int x,y; LL d; 44 len=0; 45 memset(last,0,sizeof(last)); 46 scanf("%d%d",&n,&m); 47 for (int i=1;i<=m;i++) 48 { 49 scanf("%d%d%lld",&x,&y,&d); 50 ins(x,y,d); 51 if (d>=0) ins(y,x,d); 52 } 53 cir=false; 54 for (int i=1;i<=n;i++) 55 { 56 for (int j=1;j<=n;j++) dis[j]=INF; 57 memset(vis,0,sizeof(vis)); 58 dis[i]=0, dfs(i); 59 if (cir) break; 60 } 61 if (cir) printf("YE5\n"); 62 else printf("N0\n"); 63 } 64 return 0; 65 }
3.spfa_dfs+优化,既然是找负环,那么就是找权和为负数的回路。P.S.这个一定要理解!!ヾ(。 ̄□ ̄)ツ゜゜゜我想了很久......网上也没搜到。负环负环,不单单是含负权边的环,而是权和为负的环。原因是对于一个在负环里的点,只有经过负环后的距离还比原来的小,才会再一次访问该点。要不怎么会一直循环走这个环,直到RE呢......ヘ(_ _ヘ) 也就是环的权和为负数!
实现就是我们对 dis 数组清零,再权和为负的情况下做spfa,且使用 dfs 判断成环。时间复杂度远远小于O(nm)。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6 typedef long long LL; 7 8 const int N=200010,D=200010; 9 const LL INF=(LL)5e10; 10 int n,m,len; 11 bool cir; 12 int last[N],vis[N]; 13 LL dis[N]; 14 struct edge{int x,y,next;LL d;}a[2*N]; 15 16 void ins(int x,int y,LL d) 17 { 18 a[++len].x=x,a[len].y=y,a[len].d=d; 19 a[len].next=last[x],last[x]=len; 20 } 21 void dfs(int x) 22 { 23 if (vis[x]) {cir=true;return;} 24 vis[x]=1; 25 for (int i=last[x];i;i=a[i].next) 26 { 27 int y=a[i].y; 28 if (dis[x]+a[i].d<dis[y]) 29 { 30 dis[y]=dis[x]+a[i].d; 31 dfs(y); 32 if (cir) return; 33 } 34 } 35 vis[x]=0; 36 } 37 int main() 38 { 39 int T; 40 scanf("%d",&T); 41 while (T--) 42 { 43 int x,y; LL d; 44 len=0; 45 memset(last,0,sizeof(last)); 46 scanf("%d%d",&n,&m); 47 for (int i=1;i<=m;i++) 48 { 49 scanf("%d%d%lld",&x,&y,&d); 50 ins(x,y,d); 51 if (d>=0) ins(y,x,d); 52 } 53 memset(dis,0,sizeof(dis)); 54 memset(vis,0,sizeof(vis)); 55 cir=false; 56 for (int i=1;i<=n;i++) 57 { 58 dfs(i); 59 if (cir) break; 60 } 61 if (cir) printf("YE5\n"); 62 else printf("N0\n"); 63 } 64 return 0; 65 }