D113【模板】最短路→负环 SPFA 算法 P2850 [USACO06DEC] Wormholes G 虫洞

D113 最短路→负环 SPFA 算法 P2850 [USACO06DEC] 虫洞 Wormholes G_哔哩哔哩_bilibili

 

P3385 【模板】负环 - 洛谷

P2850 [USACO06DEC] Wormholes G - 洛谷

图中有无向边和有向边,判断图中是否存在负环。

思路

因为有负边权,不能用 Dijkstra,只好选 SPFA

判负环的原理:

用 $cnt[u]$ 记录从起点到 $u$ 点的最短路经过的边数

如果出现 $cnt[u]\ge n$,则表示该图中一定存在负环

因为现在走了 $n$ 条边,对应 $n+1$ 个点,说明一定有点是重复经过的

只从一个点开始,可能走不到环,所以开始时将所有点插入队列中

  image

相关板子:

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");
  }
}

 

posted @ 2026-03-25 14:29  董晓  阅读(73)  评论(0)    收藏  举报