Floyd算法
問題的提出:已知一個有向網(或者無向網),對每一對定點vi!=vj,要求求出vi與vj之間的最短路徑和最短路徑的長度。
解決該問題有以下兩種方法:
(1)輪流以每一個定點為源點,重復執行Dijkstra算法或者Bellman-Ford算法n次,就可以求出每一對頂點之間的最短路徑和最短路徑的長度,總的時間復雜度為O(n^3)。
(2)采用Floyd算法,時間復雜度也是O(n^3),但是形式更為直接。
1.介紹
floyd算法只有五行代碼,代碼簡單,三個for循環就可以解決問題,所以它的時間復雜度為O(n^3),可以求多源最短路問題。
2.思想:
Floyd算法的基本思想如下:從任意節點A到任意節點B的最短路徑不外乎2種可能,1是直接從A到B,2是從A經過若干個節點X到B。所以,我們假設Dis(AB)為節點A到節點B的最短路徑的距離,對於每一個節點X,我們檢查Dis(AX) + Dis(XB) < Dis(AB)是否成立,如果成立,證明從A到X再到B的路徑比A直接到B的路徑短,我們便設置Dis(AB) = Dis(AX) + Dis(XB),這樣一來,當我們遍歷完所有節點X,Dis(AB)中記錄的便是A到B的最短路徑的距離。
舉個例子:已知下圖,
如現在只允許經過1號頂點,求任意兩點之間的最短路程,只需判斷e[i][1]+e[1][j]是否比e[i][j]要小即可。e[i][j]表示的是從i號頂點到j號頂點之間的路程。e[i][1]+e[1][j]表示的是從i號頂點先到1號頂點,再從1號頂點到j號頂點的路程之和。其中i是1~n循環,j也是1~n循環,代碼實現如下。
1 for(i=1; i<=n; i++) 2 { 3 for(j=1; j<=n; j++) 4 { 5 if ( e[i][j] > e[i][1]+e[1][j] ) 6 e[i][j] = e[i][1]+e[1][j]; 7 } 8 }
接下來繼續求在只允許經過1和2號兩個頂點的情況下任意兩點之間的最短路程。在只允許經過1號頂點時任意兩點的最短路程的結果下,再判斷如果經過2號頂點是否可以使得i號頂點到j號頂點之間的路程變得更短。即判斷e[i][2]+e[2][j]是否比e[i][j]要小,代碼實現為如下。
1 //經過1號頂點 2 for(i=1; i<=n; i++) 3 for(j=1; j<=n; j++) 4 if (e[i][j] > e[i][1]+e[1][j]) 5 e[i][j]=e[i][1]+e[1][j]; 6 //經過2號頂點 7 for(i=1; i<=n; i++) 8 for(j=1; j<=n; j++) 9 if (e[i][j] > e[i][2]+e[2][j]) 10 e[i][j]=e[i][2]+e[2][j];
最后允許通過所有頂點作為中轉,代碼如下:
1 for(k=1; k<=n; k++)///Floyd-Warshall算法核心語句 2 { 3 for(i=1; i<=n; i++) 4 { 5 for(j=1; j<=n; j++) 6 { 7 if(map[i][j]>map[i][k]+map[k][j] ) 8 { 9 map[i][j]=map[i][k]+map[k][j]; 10 } 11 } 12 } 13 }
這段代碼的基本思想就是:最開始只允許經過1號頂點進行中轉,接下來只允許經過1和2號頂點進行中轉……允許經過1~n號所有頂點進行中轉,求任意兩點之間的最短路程。與上面相同
3.代碼模板:
1 #include <stdio.h> 2 #define inf 0x3f3f3f3f 3 int map[1000][1000]; 4 int main() 5 { 6 int k,i,j,n,m;///n表示頂點個數,m表示邊的條數 7 scanf("%d %d",&n,&m); 8 for(i=1; i<=n; i++)///初始化 9 { 10 for(j=1; j<=n; j++) 11 { 12 if(i==j) 13 map[i][j]=0; 14 else 15 map[i][j]=inf; 16 } 17 } 18 int a,b,c; 19 for(i=1; i<=m; i++)///有向圖 20 { 21 scanf("%d %d %d",&a,&b,&c); 22 map[a][b]=c; 23 } 24 for(k=1; k<=n; k++)///Floyd-Warshall算法核心語句 25 { 26 for(i=1; i<=n; i++) 27 { 28 for(j=1; j<=n; j++) 29 { 30 if(map[i][j]>map[i][k]+map[k][j] ) 31 { 32 map[i][j]=map[i][k]+map[k][j]; 33 } 34 } 35 } 36 } 37 for(i=1; i<=n; i++)///輸出最終的結果,最終二維數組中存的即使兩點之間的最短距離 38 { 39 for(j=1; j<=n; j++) 40 { 41 printf("%10d",map[i][j]); 42 } 43 printf("\n"); 44 } 45 return 0; 46 }