本文全文參考了 《算法競賽進階指南》by lydrainbowcat
如果一個環上所有邊的權值和爲負數,稱其爲負環。
常用的能夠判斷負環的算法一般就是 Bellman-Ford 系的最短路算法。
若圖中存在負環,那麼直觀表現爲:總是存在一條邊 \((x,y)\), 使得 \(\sf d[y]\le d[x]+w(x,y)\) 不被滿足。根據抽屜原理, 若源點到節點 \(x\) 的最短路包含 \(\ge n\) 條邊, 那麼這條路徑必然重複經過了某個點 \(p\), 如果這個環是正環, 顯然去掉這個環後會得到一條更短的最短路, 與假設矛盾, 於是這個環必定是負環, 由負環的性質知從源點到節點 x 不存在最短路。
Bellman-Ford 算法判定負環
若經過 n 次迭代後, 算法未結束, 則圖中存在負環, 反之不存在。
示例代碼, 參考了 OI-wiki:
bool Bellman_Ford() {
for(int i=0; i<n; ++i) {
bool jud = false;
for(int j=1; j<=n; ++j)
for(int k=h[j]; ~k; k=nxt[k])
if(dist[p[k]] > dist[j] + w[k])
dist[p[k]] = dist[p[k]] + w[k],
jud = true;
if(!jud) break;
}
for(int j=1; j<=n; ++j)
for(int k=h[j]; ~k; k=nxt[k])
if(dist[p[k]] > dist[j] + w[k])
return true;
return false;
}
隊列優化的 Bellman-Ford 判負環:
設 \(cnt[x]\) 表示從源點到 \(x\) 的最短路經過的邊數, 在最短路執行的過程中維護其, 不難發現可以輕鬆地判斷某一時刻是否有某個 \(x\) 滿足 \(cnt[x]\ge n\), 就可以判負環了。
另一種方法是記錄每個點入隊的次數,次數達到 n 的時候說明有負環。(我還不懂這個)