1. 程式人生 > 實用技巧 >SPFA判斷負環BFS+DFS

SPFA判斷負環BFS+DFS

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