floyd演算法學習筆記
如有錯誤,歡迎各位dalao
批評指出。
前置芝士:
1.鄰接矩陣(floyd
要用鄰接矩陣存圖)
2.動態規劃思想(最好學過,沒學過也沒有太大影響)
我們可以發現,如dijkstra,spfa,bellman ford
一類的最短路演算法都是解決單源點最短路問題,也就是確定了起點或者終點來求最短路的問題。但是,我們發現,這些演算法解決多源點最短路問題,也就是有多個起點和終點的最短路問題 ,的效率太低。假設有 \(n\) 個點,\(m\) 條邊。解決多源最短路時,如果用以上三種演算法來解決,都需要分別做 \(n\) 次,來求解以每個點為起點的單源最短路,時間複雜度最慢分別是 \(o(nmlogn),o(n^2m),o(n^2m)\),在稠密圖 \(m=n*(n-1)/2≈n^2\)其中最快的都需要 \(o(n^3logm)\) ,最慢的甚至是 \(o(n^4)\) ,效率太低。因此,我們今天的主角floyd
就因解決多源最短路問題而誕生了!
floyd
演算法的基本思想是動態規劃。
設 \(dp_\) 表示 \(i\) 到 \(j\) 的最短距離。(有 \(n\) 個點)
首先對於這個 \(dp\) 陣列的初始化就是將輸入的邊 \(x-y\) 權值為 \(z\) (無權圖就是 \(1\)),如果圖是無向,則 \(dp_=dp_=z\) ,如果圖是有向,則 \(dp_=z\),最後將所有 \(dp_=0 (0\le i\le n)\),比較顯然,這裡不做解釋。
接著我們進行狀態轉移。顯然,我們要轉移 \(dp_\),就需要找乙個點 \(k\),來進行轉移,也就是 \(dp_\gets min(dp_,dp_+dp_)\),其中 \(dp_+dp_\) 就表示 \(i-k\) 的最短路與 \(j-k\) 的最短路之和,其實也就相當於乙個鬆弛操作。
這裡特別要注意的是:我們的 \(k\) 那一層迴圈一定要放在 \(i\) 和 \(j\) 兩層迴圈之外,因為如果放在 \(i,j\) 以內的話,你就會發現每兩個點的最短路只會被算到一次,而當你在進行狀態轉移時,你只算到一次的話,你會發現有些點在轉移時,還沒有被更新,就會出現沒有求出最短路的情況,所以,\(k\) 的迴圈要放到 \(i,j\) 的迴圈之外。
floyd
演算法由於 \(i,j,k\) 都要列舉一層迴圈,所以時間複雜度為 \(o(n^3)\),比開頭講的三個演算法要快。
//n表示有n個點
memset(dp,0x3f,sizeof(dp));//賦乙個極大值,並且防止在轉移時不溢位。
//接下來進行邊的初始化和dp[i][i]=0
............
//狀態轉移
for(int k=1;k<=n;++k)//列舉k
}}
(1).floyd 輸出路徑對於輸出路徑,我們可以定義乙個 \(path_\) 表示 \(dp_\) 是有 \(dp_}+dp_j}\) 轉移而來。並且在一開始,我們將所有 \(path\) 陣列的元素賦乙個特殊值。(假定為 \(-1\) )
顯然,要輸出 \(i-j\) 的路徑,我們可以通過遞迴來解決。
具體**如下:
//賦特殊值並且做 floyd
-----------
//輸出路徑函式
void print(int i,int j)//print(a,b) 表示輸出a,b的最短路徑
(2).floyd 判斷負環在說這乙個應用之前,我們先來講一下負環的定義。
負環,就是指乙個圖中,存在乙個環,使得這個環的權值之和為負數,這個環就是負環。
例如下圖:
可以發現,由圖中三個點組成的乙個環的權值之和為負數,這就是乙個簡單的負環。
乙個圖中一旦存在負環,環裡的兩個點之間的最短路可以被無限更新,因為為了使得路徑長度最短,它可以一直走這個負環,無限迴圈,這個最短路徑就可以無限縮小。也就是說,乙個圖一旦存在負環,它的最短路就是 \(-\infty\) ,也就相當於無解。
雖然說spfa bellmanford
兩個最短路演算法可以判斷負環,但是我們還是要講一講floyd
演算法判斷負環的方法。
因為負環可以使得兩個點之間的最短路無限變小,所以我們可以發現,我們可以做兩次floyd
,第一次來更新所謂的最短路,然後再做第二次的時候,如果兩個點之間的最短路還可以被更新,就可以說明這個圖中存在負環,比較顯然。(自己想一想)。
第二次floyd
**實現:
for(int k=1;k<=n;++k)}}
}
(3).floyd 判斷有向圖的連通性我們知道,對於無向圖的兩個點是否聯通,我們可以運用並查集來判斷兩個點是否聯通。
但是對於乙個有向圖,並查集是不能夠維護的,所以這個時候,我們就再一次請出今天的主角floyd
來判斷兩個點 \(i,j\) 是否連通。
我們定義 \(dp\) 表示 \(i,j\) 是否連通,若聯通則為 \(1\) ,不連通則為 \(0\) 。
顯然,我們可以根據floyd
一一樣的方式進行初始化。一開始除了 \(dp_\) 全部賦值為 \(0\) ,輸入邊的時候直接初始化即可。
接下來就是狀態轉移。我們可以模仿普通的floyd
,再找乙個點 \(k\) ,如果 \(i\) 可以到達 \(k\) ,\(k\) 可以到達 \(j\) ,就可以說明 \(i\) 可以到達 \(j\) ,轉化成轉移方程就是 \(dp_|=dp_\&dp_\)。
核心**:
//n表示有n個點
memset(dp,0,sizeof(dp));//賦值。
//接下來進行邊的初始化和dp[i][i]=1
............
//狀態轉移
for(int k=1;k<=n;++k)//列舉k
}}
關於floyd
演算法的講解就到這裡了,\(see~ you\) 。 Floyd演算法學習
今天做了個題,leetcode1462,對於同乙個圖多次查詢最短路徑,然後用dfs超時了,之後就不會了,看題解是用floyd。但是這個演算法看起來簡單,我想了很久,主要是這個dp的無後效性想了很久,現在差不多明白了,之前怕的後效性指的是,當第k個點作為中繼點將某兩個 設為ij 點鬆弛之後,ik和kj...
演算法 學習筆記
1.輸入輸出演算法至少有乙個或多個輸出 2.有窮性 3.確定性 4.可行性 1.正確性a.演算法程式沒有語法錯誤 b.演算法程式對於合法的輸入資料能夠產生滿足要求的輸出結果 c.演算法程式對於非法的輸入資料能夠得出滿足規格說明的結果 d.演算法對於精心選擇的,甚至刁難的測試資料都有滿足要求的輸出結果...
演算法學習筆記
複雜度分析 1.只關注迴圈次數最多的一行 2.總複雜度等於量級最大 的複雜度 3.巢狀 的複雜度等於巢狀 內外複雜度的乘積 單鏈表結構和順序儲存結構的優缺點 儲存分配方式 時間效能 空間效能 單鏈表結構 用一組任意的儲存單元存放線性表元素 查詢 o n 插入和刪除 找到某位置的指標後,插入和刪除的時...