D113【模板】最短路→负环 SPFA 算法 P2850 [USACO06DEC] Wormholes G 虫洞
D113 最短路→负环 SPFA 算法 P2850 [USACO06DEC] 虫洞 Wormholes G_哔哩哔哩_bilibili
P2850 [USACO06DEC] Wormholes G - 洛谷
图中有无向边和有向边,判断图中是否存在负环。
思路
因为有负边权,不能用 Dijkstra,只好选 SPFA
判负环的原理:
用 $cnt[u]$ 记录从起点到 $u$ 点的最短路经过的边数
如果出现 $cnt[u]\ge n$,则表示该图中一定存在负环
因为现在走了 $n$ 条边,对应 $n+1$ 个点,说明一定有点是重复经过的
只从一个点开始,可能走不到环,所以开始时将所有点插入队列中

相关板子:
D03【模板】最短路 Bellman-Ford 算法 SPFA 算法 P3385 负环 - 董晓 - 博客园
// 负环 SPFA 算法 O(kM~NM) #include<bits/stdc++.h> using namespace std; const int N=510,M=5210; int idx,h[N],to[M],ww[M],ne[M]; void add(int a,int b,int c){ to[++idx]=b,ww[idx]=c,ne[idx]=h[a],h[a]=idx; } int n,m1,m2; int d[N],cnt[N]; bool vis[N]; bool spfa(){ memset(d,0,sizeof d); memset(cnt,0,sizeof cnt); memset(vis,0,sizeof vis); queue<int> q; for(int i=1; i<=n; i++) q.push(i),vis[i]=true; while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=false; for(int i=h[u]; i; i=ne[i]){ int v=to[i]; if(d[v]>d[u]+ww[i]){ d[v]=d[u]+ww[i]; cnt[v]=cnt[u]+1; //记录走过的边数 if(cnt[v]==n) return true; //有负环 if(!vis[v]) q.push(v), vis[v]=true; } } } return false; //无负环 } int main(){ int T; scanf("%d",&T); for(int a,b,c;T--;){ scanf("%d%d%d",&n,&m1,&m2); idx=0; memset(h,0,sizeof h); for(int i=0; i<m1; i++){ scanf("%d%d%d",&a,&b,&c); add(a,b,c),add(b,a,c); } for(int i=0; i<m2; i++){ scanf("%d%d%d",&a,&b,&c); add(a,b,-c); //有向边 } if(spfa()) puts("YES"); else puts("NO"); } }
浙公网安备 33010602011771号