AcWing 904 蟲洞
阿新 • • 發佈:2022-03-26
\(SPFA\)判斷負環的祼題,模板題
#include <bits/stdc++.h> using namespace std; const int N = 510, M = 5210; int n, m1, m2; int dist[N]; // dist[x]記錄源點到x的最短距離 int cnt[N]; // cnt[x]記錄源點到x在產生最短距離時的邊數 bool st[N]; //鄰接表 int e[M], h[N], idx, w[M], ne[M]; void add(int a, int b, int c) { e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; } bool spfa() { /*注意:這句話在判斷是不是負環時,寫不寫都是可以AC的 為什麼呢?原因很簡單,因為就算是都是0,如果有負環,肯定在負環處會一次次進入 佇列,直到cnt[x]>=n,也是一樣可以得到正確答案的,但為了程式碼的標準與一致性, 還是要求寫上 memset(dist,0x3f,sizeof dist); */ //初始化距離INF memset(dist, 0x3f, sizeof dist); //是不是已經加入到集合中 memset(st, 0, sizeof st); //初始化從源點到x的最短距離時,邊數都是0 memset(cnt, 0, sizeof cnt); queue<int> q; //底層相當於有一個虛擬源點0 // 0到 [1,n]的所有點,邊權為0,不影響現在的圖 // 從虛擬節點0出發,到達所有的1~n,就成為了單源最短路徑問題 for (int i = 1; i <= n; i++) { q.push(i); st[i] = true; } // spfa while (q.size()) { int t = q.front(); q.pop(); st[t] = false; for (int i = h[t]; ~i; i = ne[i]) { int j = e[i]; if (dist[j] > dist[t] + w[i]) { dist[j] = dist[t] + w[i]; /* dist[j]表示j點距離源點的距離,cnt[j]表示從源點到j點經過的邊數 cnt[j] = cnt[t] + 1 的意思是 如果距離更新了,那麼從源點到j的邊數就等於源點到t的邊數 + t到x的邊數(即一條邊) 所以通過這個我們可以判斷是否存在負環,如果在j,t之間存在負環,那麼cnt[j] 會不斷加1, 我們通過判斷如果cnt[j] >= n 進而確定是否存在負環。 為什麼是cnt[j] >= n ? 因為cnt陣列表示的是邊數,如果從源點到j點的邊數大於等於n,那麼 在源點和j點之間肯定存在n+1個點,但是最多隻有n個點,根據抽屜原理,所以必然有點重複出現, 存在負環 ! */ cnt[j] = cnt[t] + 1; if (cnt[j] == n) return true; if (!st[j]) { q.push(j); st[j] = true; } } } } return false; } int main() { int T; cin >> T; while (T--) { cin >> n >> m1 >> m2; //初始化鄰接表 memset(h, -1, sizeof h); idx = 0; int a, b, c; //田地 雙向邊 while (m1--) { cin >> a >> b >> c; add(a, b, c), add(b, a, c); } //蟲洞 回到t秒前 單向負邊 while (m2--) { cin >> a >> b >> c; add(a, b, -c); } //用spfa判斷是不是有負環 if (spfa()) cout << "YES" << endl; else cout << "NO" << endl; } return 0; }