之前觉得自己写的负环不是很好,今天想再写一遍。
负环
负环,又叫负权回路,负权环,指的是一个图中存在一个环,里面包含的边的边权总和 < 0 \lt0 <0。在存在负环的图中,是求不出最短路径的,因为只要在这个环上不停的兜圈子,最短路径就会无限小。
定理:如果一个点的入队的次数大于点的总数 N N N,则存在负权回路。
若图中没有负环,则最多经过 n − 1 n-1 n−1轮迭代后算法结束。所以若第 n n n轮迭代仍有结点的最短路能被更新,则图中有负环。
所以我们用 c n t [ x ] cnt[x] cnt[x]表示 1 1 1到 x x x的最短路包含的边数(该题要求以 1 1 1作为源点), c n t [ 1 ] = 0 cnt[1]=0 cnt[1]=0。每次用 d i s [ x ] + w ( x , y ) dis[x]+w(x,y) dis[x]+w(x,y)更新 d i s [ y ] dis[y] dis[y]时,也是 c n t [ x ] + 1 cnt[x]+1 cnt[x]+1更新 c n t [ y ] cnt[y] cnt[y]。此过程中若出现 c n t [ y ] ≥ n cnt[y]≥n cnt[y]≥n,则图中有负环。最坏情况复杂度也是 O ( n m ) O(nm) O(nm)。
参考代码
#include<bits/stdc++.h>
#define in read()
#define MAXN 3003
#define MAXM 2*MAXN
using namespace std;
int T,n,m;
int nex[MAXM],first[MAXM],to[MAXM],val[MAXM],tot=0;
bool vis[MAXN];
int cnt[MAXN],dis[MAXN];
queue<int>q;
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0' or c>'9'){
if(c=='-')f=-1;c=getchar();}
while(c>='0' and c<='9'){
x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return x*f;
}
inline void addedge(int u,int v,int w){
//建图
nex[++tot]=first[u];
first[u]=tot;
to[tot]=v;
val[tot]=w;
}
inline void prework(){
//初始化
memset(first,0,sizeof(first));