前兩講討論的是給定起點,問到其他點的最短距離,稱為單源最短路徑問題,而現在我們想考慮 $V$ 中兩兩點對彼此間的最短距離,又稱全點對最短路徑問題。
一個簡單的想法是對每一個點都跑一遍單源最短路徑的 Algorithm,複雜度如下:
- Bellman-Ford: $\mathcal{O}(V*EV) = \mathcal{O}(EV^2)$
- Dijkstra (W/ Fib. Heap): $\mathcal{O}(V*(E+V \log V)) = \mathcal{O}(EV + V^2 \log V)$
Note: Dijkstra 不能處理負邊
Floyd-Warshaall Algorithm
在此我們發展一個專用於找全點對最短路徑的演算法。核心想法是將 $u \rightarrow v$ 的路徑依照經過的中繼點做分類(有/無經過),一次增加一個可用的中繼點,並更新所有路徑。
優點 (與 Bellman-Ford 一樣):
- 可以處理有負邊的圖
- 檢測是否存在負環
Dynamic Programming
$\displaystyle \Rightarrow$ Final answer: $\displaystyle d_{ij}^n$
複雜度為 $\mathcal{O}(V^3)$
Implementation
Johnson's Algorithm
相比於 Bellman-Ford 跑 $|V|$ 次的複雜度, Floyd-Warshall 可算是樂勝了;但對 Dijkstra 卻不盡然,如果說今天圖上的邊不多時(e.g $|E| = \mathcal{O}(V \log V)$),跑 Dijkstra $|V|$ 次的複雜度還比較好。\
Note: 完全圖: $|E| = \mathcal{O}(V^2)$
但注意到,Dijkstra 只能處理沒有負邊的圖,所以直接使用是不行的。而 Johnson's Algorithm 的核心概念在於重新調整邊的權重使其為非負,再來對每個節點跑Dijkstra 。
更改邊權重
Want: 不影響最短路徑及負環的位置
方法: 對於圖上每個節點賦予一個權重 $h(v)$ ,而每條邊的權重更新為
Correctness
- $s \rightarrow v$ 的最短路徑不變
- 負環還會是負環 (∵ $h(s) - h(v) = 0 , \Rightarrow , w^{\prime}(s,v) = w(s,v)$)
那麼接下來的問題是,該如何 assign $h(v)$ ,使得邊的權重均為非負呢?
決定節點權重
我們回到 Bellman-Ford 完成後(所有點均不能再被 relax )來看 ,滿足的不等式為
移項之後可以發現,用任一起點到其他節點的最短路徑值作為 $h$ ,可以滿足邊權重為非負的要求😄
Combine Together
- 先用 Bellman-Ford 跑一遍去賦予節點值,並以此更新權重
- 對每個點跑 Dijkstra , 並減去 $h(s) - h(t)$ 得到原先的最短路徑值
實做細節: 考慮 $G$ 並非 connected,我們額外加一個超級起點連接到所有節點(W/ 權重 0),確保每個邊都可被更新到。
複雜度: $\mathcal{O}(VE + V^2 \log V)$
comments powered by Disqus