【学习笔记】负环


负环

定义:给定一张有向图(无向图可以看作有向图),每条边有一个权值。若一条边的权值为负数,则称它为负边权。若图中存在一个环,环上的各边权之和为负数,则称这个环为“负环”。

——\(from\)《算法竞赛进阶指南》

而处理负边权我们就可以使用\(SPFA\)

\(cnt[x]\)表示从1到x的最短路径包含的边数,\(cnt[1]=0\),当我们更新\(d[y]=d[x]+z\)时,同时更新\(cnt[y]=cnt[x]+1\)

\(cnt[y]>=n\),则有负环,假设没有负环的情况,一共有\(n\)个点,则最短路最多经过\(n\)个点,\(n-1\)条边,一个点过2次,不是最优解

负环代码

洛谷【模板】负环

#include 
#include 
#include 
#include 
#include 
using namespace std;
const int N=2e5+5;
queue q;
bool v[N];
int ver[N],tot,head[N],cnt[N],nxt[N],d[N],edge[N],n,m,t;
inline int read(){
  int x=0,f=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
  while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
inline void add(int x,int y,int z){
  ver[++tot]=y,nxt[tot]=head[x],head[x]=tot,edge[tot]=z;
}
inline void spfa(){
  memset(d,0x3f,sizeof(d));
  memset(v,0,sizeof(v));
  memset(cnt,0,sizeof(cnt));
  while(q.size())q.pop();
  d[1]=0;v[1]=1;q.push(1);
  while(q.size()){
    int x=q.front();v[x]=0;q.pop();
    for(int i=head[x];i;i=nxt[i]){
      int y=ver[i],z=edge[i];
      if(d[y]>d[x]+z){
	d[y]=d[x]+z;
	cnt[y]=cnt[x]+1;
	if(cnt[y]>=n){
	  cout<<"YES"<=0) add(u,v,w),add(v,u,w);
      else add(u,v,w);
    }
    spfa();
  }
  return 0;
}