Floyd算法:
Floyd算法用來找出每對頂點之間的最短距離,它對圖的要求是,既可以是無向圖也可以是有向圖,邊權可以為負,但是不能存在負環(可根據最小環的正負來判定).
基本算法:
Floyd算法基於動態規划的思想,以 u 到 v 的最短路徑至少經過前 k 個點為轉移狀態進行計算,通過 k 的增加達到尋找最短路徑的目的.當 k 增加 1 時,最短路徑要么不邊,如果改變,必經過第 k 各點,也就是說當起點 u 到第 k 個點的最短距離加上第 k 個點到終點 v 的最短路徑小於不經過第 k 個節點的最優最短路經長度的時候更新 u 到 v 的最短距離. 當 k = n 時, u 到 v 的最短路徑就確定了.
偽代碼:
圖的存儲用鄰接矩陣 gra[][] 來記錄,如果 u 與 v 之間沒有邊直接相連,則 gra[u][v] = INF; dist[][] 記錄最終的最短路. pre[i][j] 存儲 i 到 j 路徑中 i 的后一個節點.
1): 初始化:將 gra 中的數據復制到 dist 中作為每對頂點間的最短路的初值, pre[i][j] = j;
2): k 從 1 到 n 循環 n 次, 每次循環中枚舉圖中不同的兩點 u, v, 如果 dist[u][v] > dist[u][k] + dist[k][v], 則更新 dist[u][v] = dist[u][k] + dist[k][v], 更新 pre[u][v] = pre[u][k].
3): 最后 dist[u][v] 數組中存儲的就是 u 到 v 的最短距離, u 到 v 的路徑, 則可以按照順序查找就好了.
以圖為例:
有一個如下的無向圖, “D”數組存儲最短路值, “P” 數組存儲最短路徑:
假設現在每對頂點之間的路徑只允許經過點 “1” , 則更新后的每對頂點之間的距離:
這里看到點 “2” 到點 “3” 的距離經過點 “1” 得到了更新,同時更新了用於記錄路徑的 P 數組.
第二步,允許每對頂點之間的最短路徑經過點 “1” 和點 “2”,則更新后的數組為:
可以看到得到更新的路徑為:
1 ---> 4, 經過點 “2” 得到更新
1 ---> 5, 經過點 “2” 得到更新
3 ---> 5. 經過點 “1 --- > 2” 得到更新
第三步: 允許經過點 “1”, “2” 和點 “3” 則更新后的數組為:
這則說明,上一步的最短路徑不需要更新.
第四步, 允許經過點 “1”, “2” , “3” 和點 “4” 則更新后的數組為:
可以看到 3 ---> 5 的路徑經過點 “4” 得到了更新(原先是 3 ---> 1 ---> 2 ---> 5, w = 9)
第五步, 允許任意兩點之間的最短路徑可以經過全部點,則更新后的數組為:
這次得到更新的路徑為:
1 ---> 4 的路徑. 更新為 “1 ---> 2 ---> 5 ---> 4, w = 5” (原路徑為 1 ---> 2 ---> 4, w = 7)
2 ---> 3 的路徑. 更新為 “2 ---> 5 ---> 4 ---> 3, w = 7” (原路經為 2 ---> 1 ---> 3, w = 8)
2 ---> 4 的路徑. 更新為 “2 --> 5 --> 4, w = 2” (原路徑為 2 ---> 4, w = 4)
無向圖反之亦然.
至此最短路徑就尋找完畢. dist[i][j] 數組里面保存的就是 i 到 j 的最短距離.如果要查尋路徑, 則按照查數組 pre 就好.比如查詢 “2” 到 “3” 的路徑:
則尋找 pre[2][3] = 5, 2 ---> 5
繼續尋找 pre[5][3] = 4, 2 ---> 5 ---> 4
繼續尋找 pre[4][3] = 3, 2 ---> 5 ---> 4 ---> 3
由於此時 i = j = 3, 則 “2” 到 “3” 的最短路徑已找到為: 2 ---> 5 ---> 4 ---> 3
1 #include <bits/stdc++.h> 2 3 typedef long long LL; 4 const int MAXN = 100; 5 const int INF = 0x3f3f3f3f; 6 using namespace std; 7 8 int pre[MAXN + 3][MAXN + 3], dist[MAXN + 3][MAXN + 3]; //pre 儲存路徑; dist 存儲最短距離 9 void floyd(int n, int gra[][MAXN + 3]) { 10 for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) dist[i][j] = gra[i][j], pre[i][j] = j; //初始化 11 for(int k = 1; k <= n; k++) { //嘗試經過 k 個點對每對頂點之間的距離進行更新 12 for(int i = 1; i <= n; i++) { 13 for(int j = 1; j <= n; j++) { 14 if(dist[i][k] != INF && dist[k][j] != INF && dist[i][k] + dist[k][j] < dist[i][j]) { 15 dist[i][j] = dist[i][k] + dist[k][j]; 16 pre[i][j] = pre[i][k]; 17 } 18 } 19 } 20 } 21 } 22 23 int pfpath(int u, int v) { //打印最短路徑 24 while(u != v) { 25 cout << u << " "; 26 u = pre[u][v]; 27 } 28 cout << u << endl; 29 } 30 31 int gra[MAXN + 3][MAXN + 3]; 32 int main() { 33 int n, m; 34 while(cin >> n >> m){ // n 個點, m 條邊 35 for(int i = 0; i <= n; i++) for(int j = -1; j <= n; j++){ 36 gra[i][j] = (i == j ? 0 : INF); 37 } 38 for(int i = 0; i < m; i++) { 39 int u, v, w; cin >> u >> v >> w; 40 gra[u][v] = gra[v][u] = w; //無向圖 41 } 42 floyd(n, gra); 43 } 44 return 0; 45 }