日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

最短路径Floyd算法【图文详解】

發布時間:2023/12/18 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 最短路径Floyd算法【图文详解】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Floyd算法

1.定義概覽

Floyd-Warshall算法(Floyd-Warshall algorithm)是解決任意兩點間的最短路徑的一種算法,可以正確處理有向圖或負權的最短路徑問題,同時也被用于計算有向圖的傳遞閉包。Floyd-Warshall算法的時間復雜度為O(N3),空間復雜度為O(N2)。

?

2.算法描述

1)算法思想原理:

???? Floyd算法是一個經典的動態規劃算法。用通俗的語言來描述的話,首先我們的目標是尋找從點i到點j的最短路徑。從動態規劃的角度看問題,我們需要為這個目標重新做一個詮釋(這個詮釋正是動態規劃最富創造力的精華所在)

????? 從任意節點i到任意節點j的最短路徑不外乎2種可能,1是直接從i到j,2是從i經過若干個節點k到j。所以,我們假設Dis(i,j)為節點u到節點v的最短路徑的距離,對于每一個節點k,我們檢查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,證明從i到k再到j的路徑比i直接到j的路徑短,我們便設置Dis(i,j) = Dis(i,k) + Dis(k,j),這樣一來,當我們遍歷完所有節點k,Dis(i,j)中記錄的便是i到j的最短路徑的距離。

2).算法描述:

a.從任意一條單邊路徑開始。所有兩點之間的距離是邊的權,如果兩點之間沒有邊相連,則權為無窮大。   

b.對于每一對頂點 u 和 v,看看是否存在一個頂點 w 使得從 u 到 w 再到 v 比己知的路徑更短。如果是更新它。

3).Floyd算法過程矩陣的計算----十字交叉法

方法:兩條線,從左上角開始計算一直到右下角 如下所示

給出矩陣,其中矩陣A是鄰接矩陣,而矩陣Path記錄u,v兩點之間最短路徑所必須經過的點

相應計算方法如下:

最后A3即為所求結果

3.算法代碼實現

1 typedef struct 2 { 3 char vertex[VertexNum]; //頂點表 4 int edges[VertexNum][VertexNum]; //鄰接矩陣,可看做邊表 5 int n,e; //圖中當前的頂點數和邊數 6 }MGraph; 7 8 void Floyd(MGraph g) 9 { 10   int A[MAXV][MAXV]; 11   int path[MAXV][MAXV]; 12   int i,j,k,n=g.n; 13   for(i=0;i<n;i++) 14   for(j=0;j<n;j++) 15   {    16 A[i][j]=g.edges[i][j]; 17    path[i][j]=-1; 18   } 19   for(k=0;k<n;k++) 20   { 21   for(i=0;i<n;i++) 22   for(j=0;j<n;j++) 23   if(A[i][j]>(A[i][k]+A[k][j])) 24   { 25   A[i][j]=A[i][k]+A[k][j]; 26   path[i][j]=k; 27   } 28  } 29 }

算法時間復雜度:O(n3)

對上面代碼還可以進一步優化?答案是肯定的,不信你看下面這個小故事告訴你訣竅:

? ? ? ?暑假,小哼準備去一些城市旅游。有些城市之間有公路,有些城市之間則沒有,如下圖。為了節省經費以及方便計劃旅程,小哼希望在出發之前知道任意兩個城市之前的最短路程。



? ? ? ?上圖中有4個城市8條公路,公路上的數字表示這條公路的長短。請注意這些公路是單向的。我們現在需要求任意兩個城市之間的最短路程,也就是求任意兩個點之間的最短路徑。這個問題這也被稱為“多源最短路徑”問題。

?

? ? ? ?現在需要一個數據結構來存儲圖的信息,我們仍然可以用一個4*4的矩陣(二維數組e)來存儲。比如1號城市到2號城市的路程為2,則設e[1][2]的值為2。2號城市無法到達4號城市,則設置e[2][4]的值為∞。另外此處約定一個城市自己是到自己的也是0,例如e[1][1]為0,具體如下。

? ? ? ?現在回到問題:如何求任意兩點之間最短路徑呢?通過之前的學習我們知道通過深度或廣度優先搜索可以求出兩點之間的最短路徑。所以進行n2遍深度或廣度優先搜索,即對每兩個點都進行一次深度或廣度優先搜索,便可以求得任意兩點之間的最短路徑。可是還有沒有別的方法呢?

?

? ? ? ?我們來想一想,根據我們以往的經驗,如果要讓任意兩點(例如從頂點a點到頂點b)之間的路程變短,只能引入第三個點(頂點k),并通過這個頂點k中轉即a->k->b,才可能縮短原來從頂點a點到頂點b的路程。那么這個中轉的頂點k是1~n中的哪個點呢?甚至有時候不只通過一個點,而是經過兩個點或者更多點中轉會更短,即a->k1->k2b->或者a->k1->k2…->k->i…->b。比如上圖中從4號城市到3號城市(4->3)的路程e[4][3]原本是12。如果只通過1號城市中轉(4->1->3),路程將縮短為11(e[4][1]+e[1][3]=5+6=11)。其實1號城市到3號城市也可以通過2號城市中轉,使得1號到3號城市的路程縮短為5(e[1][2]+e[2][3]=2+3=5)。所以如果同時經過1號和2號兩個城市中轉的話,從4號城市到3號城市的路程會進一步縮短為10。通過這個的例子,我們發現每個頂點都有可能使得另外兩個頂點之間的路程變短。好,下面我們將這個問題一般化。

?

? ? ? ?當任意兩點之間不允許經過第三個點時,這些城市之間最短路程就是初始路程,如下。

? ? ? ?如現在只允許經過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號頂點的情況下,任意兩點之間的最短路程更新為:

?

? ? ? ?通過上圖我們發現:在只通過1號頂點中轉的情況下,3號頂點到2號頂點(e[3][2])、4號頂點到2號頂點(e[4][2])以及4號頂點到3號頂點(e[4][3])的路程都變短了。

?

? ? ? ?接下來繼續求在只允許經過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]) e[i][j]=e[i][1]+e[1][j]; 5 //經過2號頂點 6 for(i=1;i<=n;i++) 7 for(j=1;j<=n;j++) 8 if (e[i][j] > e[i][2]+e[2][j]) e[i][j]=e[i][2]+e[2][j];

?

在只允許經過1和2號頂點的情況下,任意兩點之間的最短路程更新為:

?

? ? ? ?通過上圖得知,在相比只允許通過1號頂點進行中轉的情況下,這里允許通過1和2號頂點進行中轉,使得e[1][3]和e[4][3]的路程變得更短了。

?

? ? ? ?同理,繼續在只允許經過1、2和3號頂點進行中轉的情況下,求任意兩點之間的最短路程。任意兩點之間的最短路程更新為:

?

? ? ? ?最后允許通過所有頂點作為中轉,任意兩點之間最終的最短路程為:

?

? ? ? ?整個算法過程雖然說起來很麻煩,但是代碼實現卻非常簡單,核心代碼只有五行:

?

1 for(k=1;k<=n;k++) 2 for(i=1;i<=n;i++) 3 for(j=1;j<=n;j++) 4 if(e[i][j]>e[i][k]+e[k][j]) 5 e[i][j]=e[i][k]+e[k][j];

?

這段代碼的基本思想就是:最開始只允許經過1號頂點進行中轉,接下來只允許經過1和2號頂點進行中轉……允許經過1~n號所有頂點進行中轉,求任意兩點之間的最短路程。用一句話概括就是:從i號頂點到j號頂點只經過前k號點的最短路程。其實這是一種“動態規劃”的思想!

動態轉移方程:dp[k][i][j]=min(dp[k-1][i][j],dp[k-1][i][k]+dp[k-1][k][j]);

下面給出這個算法的完整代碼:

1 #include <stdio.h> 2 int main() 3 { 4 int e[10][10],k,i,j,n,m,t1,t2,t3; 5 int inf=99999999; //用inf(infinity的縮寫)存儲一個我們認為的正無窮值 6 //讀入n和m,n表示頂點個數,m表示邊的條數 7 scanf("%d %d",&n,&m); 8 9 //初始化 10 for(i=1;i<=n;i++) 11 for(j=1;j<=n;j++) 12 if(i==j) e[i][j]=0; 13 else e[i][j]=inf; 14 //讀入邊 15 for(i=1;i<=m;i++) 16 { 17 scanf("%d %d %d",&t1,&t2,&t3); 18 e[t1][t2]=t3; 19 } 20 21 //Floyd-Warshall算法核心語句 22 for(k=1;k<=n;k++) 23 for(i=1;i<=n;i++) 24 for(j=1;j<=n;j++) 25 if(e[i][j]>e[i][k]+e[k][j] ) 26 e[i][j]=e[i][k]+e[k][j]; 27 28 //輸出最終的結果 29 for(i=1;i<=n;i++) 30 { 31 for(j=1;j<=n;j++) 32 { 33 printf("%10d",e[i][j]); 34 } 35 printf("\n"); 36 } 37 38 return 0; 39 }

?有一點需要注意的是:如何表示正無窮。我們通常將正無窮定義為99999999,因為這樣即使兩個正無窮相加,其和仍然不超過int類型的范圍(C語言int類型可以存儲的最大正整數是2147483647)。在實際應用中最好估計一下最短路徑的上限,只需要設置比它大一點既可以。例如有100條邊,每條邊不超過100的話,只需將正無窮設置為10001即可。如果你認為正無窮和其它值相加得到一個大于正無窮的數是不被允許的話,我們只需在比較的時候加兩個判斷條件就可以了,請注意下面代碼中帶有下劃線的語句。

?

1 //Floyd-Warshall算法核心語句 2 for(k=1;k<=n;k++) 3 for(i=1;i<=n;i++) 4 for(j=1;j<=n;j++) 5 if(e[i][k]<inf && e[k][j]<inf && e[i][j]>e[i][k]+e[k][j]) 6 e[i][j]=e[i][k]+e[k][j];

?

上面代碼的輸入數據樣式為:

?

1 4 8 2 1 2 2 3 1 3 6 4 1 4 4 5 2 3 3 6 3 1 7 7 3 4 1 8 4 1 5 9 4 3 12

?

第一行兩個數為n和m,n表示頂點個數,m表示邊的條數。

接下來m行,每一行有三個數t1、t2 和t3,表示頂點t1到頂點t2的路程是t3。 得到最終結果如下:

?

? ? ? 通過這種方法我們可以求出任意兩個點之間最短路徑。它的時間復雜度是O(N3)。令人很震撼的是它竟然只有五行代碼,實現起來非常容易。正是因為它實現起來非常容易,如果時間復雜度要求不高,使用Floyd-Warshall來求指定兩點之間的最短路或者指定一個點到其余各個頂點的最短路徑也是可行的。當然也有更快的算法,請看下一節:Dijkstra算法。

?

? ? ? ?另外需要注意的是:Floyd-Warshall算法不能解決帶有“負權回路”(或者叫“負權環”)的圖,因為帶有“負權回路”的圖沒有最短路。例如下面這個圖就不存在1號頂點到3號頂點的最短路徑。因為1->2->3->1->2->3->…->1->2->3這樣路徑中,每繞一次1->-2>3這樣的環,最短路就會減少1,永遠找不到最短路。其實如果一個圖中帶有“負權回路”那么這個圖則沒有最短路。

?

? ? ? ?此算法由Robert W. Floyd(羅伯特·弗洛伊德)于1962年發表在“Communications of the ACM”上。同年Stephen Warshall(史蒂芬·沃舍爾)也獨立發表了這個算法。Robert W.Floyd這個牛人是朵奇葩,他原本在芝加哥大學讀的文學,但是因為當時美國經濟不太景氣,找工作比較困難,無奈之下到西屋電氣公司當了一名計算機操作員,在IBM650機房值夜班,并由此開始了他的計算機生涯。此外他還和J.W.J. Williams(威廉姆斯)于1964年共同發明了著名的堆排序算法HEAPSORT。堆排序算法我們將在第七章學習。Robert W.Floyd在1978年獲得了圖靈獎。

?

?

?

?

?

?

轉載于:https://www.cnblogs.com/ECJTUACM-873284962/p/6995648.html

總結

以上是生活随笔為你收集整理的最短路径Floyd算法【图文详解】的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。