SPFA判斷負環BFS+DFS
阿新 • • 發佈:2020-08-08
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 次,
程式碼(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); }