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);
}
posted on
浙公网安备 33010602011771号