1. 程式人生 > >最短路最長路整理

最短路最長路整理

發現很多東西不記下來過一段時間就想不明白了,先整理一部分,嗯不對的地方請路過的同學指正~

首先,最短路徑相關演算法通常依賴一個重要性質,即最短路徑的子路徑也是最短路。證明如下:


單源最短路:

1、Bellman-Fold演算法 (將權值變為相反數,可以求最長路)

陣列dis[i]]記錄從源點s到頂點i的路徑長度,初始化陣列dis[i]為正無窮, dis[s]為0;

以下操作迴圈執行至多n-1次,n為頂點數:

這是因為最短路從1到n,最多經過n-1條邊,而最上邊每一層for迴圈,實際上是求從源點出發,只經過k條邊最短路是多少,k最多n-1

對於每一條邊e(u, v),如果dis[u] + w(u, v) < dis[v],則另dis[v] = dis[u]+w(u, v)。w(u, v)為邊e(u,v)的權值;

若上述操作沒有對Distant進行更新,說明最短路徑已經查詢完畢,或者部分點不可達,跳出迴圈(用flag標記優化)。否則執行下次迴圈;

為了檢測圖中是否存在負環路,即權值之和小於0的環路。對於每一條邊e(u, v),如果存在dis[u] + w(u, v) < dis[v]的邊,則圖中存在負環路,即是說改圖無法求出單源最短路徑。否則陣列dis[n]中記錄的就是源點s到各頂點的最短路徑長度。

複雜度為O(VE),最短路可以處理負環。

2、DAG(有向無環圖) Shortest Path (可以求最長路)

因為是無環圖,可以首先進行拓撲排序,得到一個所有點的線性排列,這一步複雜度為O(V+E)

然後根據拓撲排序的次序對每一個節點進行處理:即對從該節點出發的所有邊進行鬆弛操作。總複雜度為O(V+E)

poj 3249

http://www.cnblogs.com/yanlingyin/archive/2011/11/12/2246716.html 最長路標記路徑

3、Dijkstra (不能改為最長路)

堆優化時間複雜度可為O(N*logN)

4、SPFA (權值取負,可以求最長路)

可以處理負權邊,即進隊次數超過N次為出現負環。每次從佇列中取點進行鬆弛操作。

複雜度為O(KE),一般情況下K小於等於2。

SPFABellman-Ford演算法優化的關鍵之處在於意識到:只有那些在前一遍鬆弛中改變了距離估計值的點,才可能引起他們的鄰接點的距離估計值的改變

。因此,用一個先進先出的佇列來存放被成功鬆弛的頂點。初始時,源點s入隊。當佇列不為空時,取出對首頂點,對它的鄰接點進行鬆弛。如果某個鄰接點鬆弛成功,且該鄰接點不在佇列中,則將其入隊。經過有限次的鬆弛操作後,佇列將為空,演算法結束。SPFA演算法的實現,需要用到一個先進先出的佇列 queue 和一個指示頂點是否在佇列中的標記陣列 mark。為了方便查詢某個頂點的鄰接點,圖採用臨界表儲存。

多源最短路:

1、Floyd演算法 (可以求最長路,但是有可能存在正環最長路不存在,無法判斷)

從任意節點i到任意節點j的最短路徑不外乎2種可能,1是直接從i到j,2是從i經過若干個節點k到j。所以,我們假設Dis(i,j)為節點u到節點v的最短路徑的距離,對於每一個節點k,我們檢查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,證明從i到k再到j的路徑比i直接到j的路徑短,我們便設定Dis(i,j) = Dis(i,k) + Dis(k,j),這樣一來,當我們遍歷完所有節點k,Dis(i,j)中記錄的便是i到j的最短路徑的距離。

最開始初始化dis[i][j]為直接相連的權值

最外層的for迴圈實際上表示的是:僅利用點1到點k從i到j的最短路是多少。所以三層for迴圈都是從點1到點n

時間複雜度O(n^3),空間複雜度O(n^2)

同時還可以加入path[]陣列儲存路徑

http://blog.csdn.net/ll365594480/article/details/6792096

變形:floyd變形求最小環,這個連結講的很清楚

http://www.cnblogs.com/Yz81128/archive/2012/08/15/2640940.html

例題:poj 1734

應用:

1、最短路解決差分約束問題


可行解的求法


2、單源最短路(最樸素)

3、多元起點單源目的地:反向加邊處理

4、所有結對點最短路:fold-warshall

5、各演算法儲存最短路路徑