1. 程式人生 > >SPFA 最短路演算法 求負環(POJ3259)多圖負環

SPFA 最短路演算法 求負環(POJ3259)多圖負環

什麼是 SPFA

Bellman-ford的佇列優化,即SPFA

  1. 本質思想:每次從佇列中取出一個點,利用這個點出發的所有邊更新所有的終點距離,若更新成功,且被更新的點不在佇列裡,就把它放入佇列。
  2. 實現:佇列初始只放第一個點,也是用一個dis陣列,然後跑個不停。再用一個布林陣列記錄一個點在不在佇列裡。
  3. 若一個點入隊了大於N次,則存在負環。
  4. 一般不會用鄰接矩陣寫……但也可以用

上傳一張大佬圖解

上傳一張大佬圖解SPFA

附上大佬部落格連結: SPFA.

然後附上題目連結:Wormholes

題意:有 f 個農場,每個農場有 n 塊地,其間有 m 條路,w條時光隧道,過去的時候時間會倒退 t 秒。判斷是否可以從某塊地出發後又回來,看到了離開之前的自己。
注意:前m條路為雙向邊,時空隧道為單向邊。
方法:Floyd或者SPFA判斷是否存在負權環。

AC程式碼

#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#define inf 0x3f3f3f3f
#define N 502 
using namespace std;

int n, m, w, Map[N][N];

int dis[N];
bool vis[N];

bool spfa(int x){
	///Shortest Path Faster Algorithm 
vis[x] = true; dis[x] = 0; int cnt[N] = {0}; queue<int> q; q.push(x); while(!q.empty()){ int t = q.front(); q.pop(); cnt[t]++; if(cnt[t] > n) return true; vis[t] = false; for(int i = 1; i <= n; i++){ if(dis[i] > dis[t] + Map[t][i]){ dis[i] = dis[t] + Map[t][i]
; if(!vis[i]){ vis[i] = true; q.push(i); } } } } return false; } void init(int n){ for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { Map[i][j] = inf; } for(int i = 1; i <= n; i++){ dis[i] = inf; vis[i] = false; } } int main() { int u,v,w_; int t; ios::sync_with_stdio(false); cin >> t; while(t--){ cin >> n >> m >> w; init(n); for(int i = 0; i < m; i++){ cin >> u >> v >> w_; if(Map[u][v] > w_) Map[u][v] = Map[v][u] = w_; } for(int i = 0; i < w; i++){ cin >> u >>v >> w_; Map[u][v] = -w_; } if(spfa(1)) cout << "YES" <<endl; else cout << "NO" << endl; } return 0; }

下面說一下我今天一上午做這道題遇到的問題

  1. 鄰接矩陣大法好,因為要判斷重邊,鄰接表不好判斷重邊
  2. 出佇列標記vis為未訪問,進棧標記為訪問
  3. 疏鬆原理更新各個點到終點的距離
  4. 最最坑的編譯器問題,用了學校的電腦,被設定了某種許可權,使測試結果與樣例結果不符,讓我一直懷疑人生 ,關愛生命,從使用自己電腦開始

補充

  • 今天非常細心的紫紫問了我如果整張圖不連通,是不是需要遍歷所有節點做spfa,我一臉懵逼,然後她告訴我神奇的加虛節點操作,加入一個節點聯通所有節點,建立權值為0的虛邊。對當前虛節點進行spfa,若入隊節點大於n+1則存在負環。
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#define inf 0x3f3f3f3f
#define N 502 
using namespace std;

int n, m, w, Map[N][N];

int dis[N];
bool vis[N];

bool spfa(int x){
	///Shortest Path Faster Algorithm 
	vis[x] = true;
	dis[x] = 0;
	int cnt[N] = {0};
	queue<int> q;
	q.push(x);
	
	while(!q.empty()){
		int t = q.front();
		q.pop();
		cnt[t]++;
		if(cnt[t] > n + 1) return true; // 修改為n+1
		vis[t] = false;
		for(int i = 1; i <= n; i++){
			if(dis[i] > dis[t] + Map[t][i]){
				dis[i] = dis[t] + Map[t][i];
				if(!vis[i]){
					vis[i] = true;
					q.push(i);
				}
			}
		}
	}
	return false;
}

void init(int n){
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++)
		{
			Map[i][j] = inf;
		}
	for(int i = 1; i <= n; i++){
		dis[i] = inf;
		vis[i] = false;
	}
}

int main()
{
	int u,v,w_;
	int t;
	ios::sync_with_stdio(false);
	cin >> t;
	while(t--){
		cin >> n >> m >> w;
		init(n);
		for(int i = 0; i < m; i++){
			cin >> u >> v >> w_;
			if(Map[u][v] > w_)
				Map[u][v] = Map[v][u] = w_;
		}
		for(int i = 0; i < w; i++){
			cin >> u >>v >> w_;
			Map[u][v] = -w_;
		}
		for(int i = 1; i <= n; i++){ //建立虛節點和虛邊
			Map[0][i] = 0;
		} 
		if(spfa(0)) cout << "YES" <<endl;
		else cout << "NO" << endl;
	}
	return 0;
}