SPFA判断负环
https://blog.csdn.net/forever_dreams/article/details/81161527
【bfs版】
首先我们要知道,对于一个不存在负环的图,从起点到任意一个点最短距离经过的点最多只有 n 个
这样的话,我们用 cnt[ i ] 表示从起点(假设就是 1)到 i 的最短距离包含点的个数,初始化 cnt[ 1 ] = 1,
那么当我们能够用点 u 松弛点 v 时,松弛时同时更新 cnt[ v ] = cnt[ u ] + 1,若发现此时 cnt[ v ] > n,那么就存在负环
还有一种方法是记录每个点的入队次数,入队次数大于 n 就说明有负环,但是这样做一般都要比上面的方法慢。举个例子,在一个由 n 个点构成的负环中,这个方法要绕环 n 次,
而上面的方法绕环 1 次就行了
代码(Yes 是存在负环,No 是不存在负环,图是联通的)
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<cstdio> #include<queue> #include<map> #include<vector> #include<set> using namespace std; const int maxn=10100; const int maxm=20050; const int INF=0x3fffffff; typedef long long LL; typedef unsigned long long ull; /* SPFA判断负环 https://blog.csdn.net/forever_dreams/article/details/81161527 【bfs版】 首先我们要知道,对于一个不存在负环的图,从起点到任意一个点最短距离经过的点最多只有 n 个 这样的话,我们用 cnt[ i ] 表示从起点(假设就是 1)到 i 的最短距离包含点的个数,初始化 cnt[ 1 ] = 1, 那么当我们能够用点 u 松弛点 v 时,松弛时同时更新 cnt[ v ] = cnt[ u ] + 1,若发现此时 cnt[ v ] > n,那么就存在负环 还有一种方法是记录每个点的入队次数,入队次数大于 n 就说明有负环,但是这样做一般都要比上面的方法慢。举个例子,在一个由 n 个点构成的负环中,这个方法要绕环 n 次, 而上面的方法绕环 1 次就行了 代码(Yes 是存在负环,No 是不存在负环,图是联通的) */ int head[maxn],to[maxm],nex[maxm],wei[maxm]; int num,n,m; int vis[maxn],dis[maxn],cnt[maxn]; bool flag; void adde(int x,int y,int z){ to[++num]=y; nex[num]=head[x]; wei[num]=z; head[x]=num; } bool spfa(int s){ memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[s]=0; queue<int> q; q.push(s); vis[s]=1; cnt[s]=1; while(!q.empty()){ int x=q.front(); q.pop(); vis[x]=0; for(int i=head[x];i;i=nex[i]){ int t=to[i]; if(dis[t]>dis[x]+wei[i]){ dis[t]=dis[x]+wei[i]; cnt[t]=cnt[x]+1; if(cnt[t]>n) return false; if(!vis[t]) { q.push(t); vis[t]=1; } } } } return true; } int main(){ scanf("%d %d",&n,&m); int x,y,z; for(int i=1;i<=m;i++){ scanf("%d %d %d",&x,&y,&z); adde(x,y,z); } flag=spfa(1); if(flag) cout<<"YES"<<endl; else cout<<"NO"<<endl; return 0; }
【dfs版】
基于 dfs 版的 SPFA 相当于是把"先进先出"的队列换成了"先进后出"的栈
也就是说,每次都以刚刚松弛过的点来松弛其他的点,如果能够松弛点 x 并且 x 还在栈中,那图中就有负环
一般来说的话,若存在负环,那么 dfs 会比 bfs 快
但是如果不存在负环,dfs 可能会严重影响求最短路的效率,要谨慎使用
void spfa(int x){ vis[x]=1; for(int i=head[x];i;i=nex[i]){ int t=to[i]; if(dis[t]>dis[x]+wei[i]){ if(vis[t]) { flag=false; return; } dis[t]=dis[x]+wei[i]; spfa(t); } } vis[x]=false; //要回溯 } int main(){ memset(dis,0x3f,sizeof(dis)); d[1]=0; spfa(1); }