1. 程式人生 > >Floyd最短路(帶路徑輸出)

Floyd最短路(帶路徑輸出)

摘要(以下內容來自百度)

Floyd演算法又稱為插點法,是一種利用動態規劃的思想尋找給定的加權圖中多源點之間最短路徑的演算法,與Dijkstra演算法類似。
該演算法名稱以創始人之一、1978年圖靈獎獲得者、斯坦福大學計算機科學系教授羅伯特·弗洛伊德命名。
簡介編輯
在電腦科學中,Floyd-Warshall演算法是一種在具有正或負邊緣權重(但沒有負週期)的加權圖中找到最短路徑的演算法。演算法的單個執行將找到所有頂點對之間的最短路徑的長度(加權)。 雖然它不返回路徑本身的細節,但是可以通過對演算法的簡單修改來重建路徑。 該演算法的版本也可用於查詢關係R的傳遞閉包,或(與Schulze投票系統相關)在加權圖中所有頂點對之間的最寬路徑。
Floyd-Warshall演算法是動態規劃的一個例子,並在1962年由Robert Floyd以其當前公認的形式出版。然而,它基本上與Bernard Roy在1959年先前發表的演算法和1962年的Stephen Warshall中找到圖形的傳遞閉包基本相同,並且與Kleene的演算法密切相關 在1956年)用於將確定性有限自動機轉換為正則表示式。演算法作為三個巢狀for迴圈的現代公式首先由Peter Ingerman在1962年描述。
該演算法也稱為Floyd演算法,Roy-Warshall演算法,Roy-Floyd演算法或WFI演算法。 [2]
核心思路編輯
路徑矩陣
通過一個圖的權值矩陣求出它的每兩點間的最短路徑矩陣。 [3]
從圖的帶權鄰接矩陣A=[a(i,j)] n×n開始,遞迴地進行n次更新,即由矩陣D(0)=A,按一個公式,構造出矩陣D(1);又用同樣地公式由D(1)構造出D(2);……;最後又用同樣的公式由D(n-1)構造出矩陣D(n)。矩陣D(n)的i行j列元素便是i號頂點到j號頂點的最短路徑長度,稱D(n)為圖的距離矩陣,同時還可引入一個後繼節點矩陣path來記錄兩點間的最短路徑。
採用鬆弛技術(鬆弛操作),對在i和j之間的所有其他點進行一次鬆弛。所以時間複雜度為O(n^3);
狀態轉移方程
其狀態轉移方程如下: map[i,j]:=min{map[i,k]+map[k,j],map[i,j]};
map[i,j]表示i到j的最短距離,K是窮舉i,j的斷點,map[n,n]初值應該為0,或者按照題目意思來做。
當然,如果這條路沒有通的話,還必須特殊處理,比如沒有map[i,k]這條路。
演算法過程編輯
1,從任意一條單邊路徑開始。所有兩點之間的距離是邊的權,如果兩點之間沒有邊相連,則權為無窮大。
2,對於每一對頂點 u 和 v,看看是否存在一個頂點 w 使得從 u 到 w 再到 v 比已知的路徑更短。如果是更新它。
把圖用鄰接矩陣G表示出來,如果從Vi到Vj有路可達,則G[i][j]=d,d表示該路的長度;否則G[i][j]=無窮大。定義一個矩陣D用來記錄所插入點的資訊,D[i][j]表示從Vi到Vj需要經過的點,初始化D[i][j]=j。把各個頂點插入圖中,比較插點後的距離與原來的距離,G[i][j] = min( G[i][j], G[i][k]+G[k][j] ),如果G[i][j]的值變小,則D[i][j]=k。在G中包含有兩點之間最短道路的資訊,而在D中則包含了最短通路徑的資訊。
比如,要尋找從V5到V1的路徑。根據D,假如D(5,1)=3則說明從V5到V1經過V3,路徑為{V5,V3,V1},如果D(5,3)=3,說明V5與V3直接相連,如果D(3,1)=1,說明V3與V1直接相連。 [4]
時間複雜度與空間複雜度編輯
時間複雜度:O(n^3);
空間複雜度:O(n^2)
優缺點分析編輯
Floyd演算法適用於APSP(All Pairs Shortest Paths,多源最短路徑),是一種動態規劃演算法,稠密圖效果最佳,邊權可正可負。此演算法簡單有效,由於三重迴圈結構緊湊,對於稠密圖,效率要高於執行|V|次Dijkstra演算法,也要高於執行|V|次SPFA演算法。
優點:容易理解,可以算出任意兩個節點之間的最短距離,程式碼編寫簡單。
缺點:時間複雜度比較高,不適合計算大量資料。 [5]

關鍵的路徑輸出

例如

具體看程式碼

程式碼

#include<bits/stdc++.h>
using namespace std;
const int inf=999999;
int mp[20][20],path[20][20];
int n,m;
void print(int a,int b){
    if(path[a][b]==-1) return;//因為開始初始化為-1,這裡就可以避免相鄰的再次輸出 
    print(a,path[a][b]);//前半部 
    cout<<path[a][b]<<"-->";//輸出該點 
    print(path[a][b],b);//後半部 
}
int main(){
  freopen("in.txt","r",stdin);
  while(cin>>n>>m){
    memset(path,-1,sizeof(path));//初始化-1 
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++) if(i==j) mp[i][j]=0;
     else mp[i][j]=inf;
   int Start,End,dis;
  for(int i=0;i<m;i++)
     {
        cin>>Start>>End>>dis;
        mp[Start][End]=dis;
 }
 //三層迴圈
 for(int k=0;k<n;k++)
 {
    //第k個點進行鬆弛 
    for(int i=0;i<n;i++)
    for(int j=0;j<n;j++)
    if(mp[i][j]>mp[i][k]+mp[k][j])//如果能夠縮短就更新距離 
    {
        mp[i][j]=mp[i][k]+mp[k][j];
        path[i][j]=k;//記錄能鬆弛的點 
     }
  } 
 cout<<"The shortest path between vertices\n";
 for(int i=0;i<n;i++)
 for(int j=0;j<n;j++)
 {
    if(mp[i][j]==inf){//兩者不通 
        cout<<i<<' '<<j;
        cout<<" These two points cannot be reached\n\n"; continue;
     }
    cout<<i<<" to "<<j<<" shortest path is "<<mp[i][j]<<endl;
    cout<<"The specific path is\n";
    cout<<i<<"-->";
    print(i,j);
    cout<<j<<' ';
    cout<<endl<<endl;
 }
}
return 0;
} 
//輸入資料
/*
10 14
0 1 45
0 2 35
0 3 50
1 2 20
1 5 90
1 8 70
2 4 50
3 4 50
5 6 20
5 7 50
5 8 50
6 0 40
6 3 40
9 8 35
*/