Floyd 演算法學習筆記

2022-09-11 13:27:29 字數 3145 閱讀 5807

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 插入和刪除 找到某位置的指標後,插入和刪除的時...