經典的多源最短路徑算法——Floyd


  Floyd算法是經典的求算多源最短路徑的算法,它的實質還是一種動態規划思想的應用。
 
一、Floyd算法的實現思想

Floyd算法是如何實現的呢,我下面做簡單說明:
  我們要求算i,j兩點間的最短距離,首先我們引入一個中間點k,看看從i到j有沒有一條經過k的通路(即i→k→j),如果有這么一條路,那么我們將目前的從i到j的距離,與從i到k再到j的距離相比較,小的那一個更新為新的從i到j的最短路。
那么用dp寫出它的狀態轉移方程有:
  
那么在代碼里我們要怎樣來實現呢?
首先需要一個矩陣來儲存各個點的關系,對於下面一個圖,我們可以得到相應的關系矩陣(數字代表由i到j的路徑長度,INF代表目前無法連通):

其中第i行第j列就代表從點i到點j的距離。
然后我們需要三層循環,最外層k遍歷中間點,里面為i,j遍歷矩陣,結合狀態轉移方程得到Floyd算法的核心代碼:
 1 void floyd()
 2 {
 3     int i,j,k;
 4     for(k=1;k<=n;k++)
 5         for(i=1;i<=n;i++)
 6             for(j=1;j<=n;j++)
 7             {
 8                 if(dp[i][j]>dp[i][k]+dp[k][j])
 9                     dp[i][j]=dp[i][k]+dp[k][j];
10             }
11 }

顯然我們可以從代碼里看出,該算法的時間復雜度為O()。

下面我們用上面的例子來模擬一下。
首先令k=1,開始遍歷i,j,對於第(i,j)個關系來說,我們要把它本身的值和第i行第k個、第j列第k個的值的和相比較,取小的作為新的值。如下圖
對於第2行第5列的值來說,它的新的值是第2行第1個值和第5列第1個值的和。按照同樣的方法我們把k=1的遍歷走完
然后我們令k=2再開始遍歷
同理,對於第1行第3列的值來說,它的新的值是第1行第2個值和第3列第2個值的和。然后我們把k=2的情況遍歷完
然后我們接着把k=3,4,5…全部遍歷完,得到我們的最短路的矩陣
Floyd算法可以求解大多數情況,但是注意Floyd算法無法求解有負權回路的狀況,如
對於這個回路來說,它每進行一次循環,最短路就會減少1,所以永遠也找不到最短路。
 
二、Floyd算法的正確性
(施工中orz)
 
三、Floyd算法的其他應用
 
1.求解傳遞閉包
將Floyd算法稍作修改便可以得到求解傳遞閉包的代碼:
1 void floyd()
2 {
3     int i,j,k;
4     for(k=1;k<=n;k++) 
5         for(i=1;i<=n;i++)
6             for(j=1;j<=n;j++)
7                 if(matrix[i][k]&&matrix[k][j])
8                     matrix[i][j]=1;
9 }

若有會Warshall算法求傳遞閉包的朋友便會發現Floyd算法和Warshall算法高度相似!

相關題目:ZOJ P4124
代碼:
 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 int n,m,matrix[105][105],num[2][105],flag=0;
 6 
 7 void floyd()
 8 {
 9     int i,j,k;
10 
11     for(k=1;k<=n;k++)      //floyd
12         for(i=1;i<=n;i++)
13             for(j=1;j<=n;j++)
14                 if(matrix[i][k]&&matrix[k][j])
15                     matrix[i][j]=1;
16 
17     for(i=1;i<=n;i++)      //判斷是否有自環
18         for(j=1;j<=n;j++)
19             if(matrix[i][j]&&matrix[j][i])
20             {
21                 flag=1;
22                 return;
23             }
24 
25     for(i=1;i<=n;i++)    //維護更新num數組
26         for(j=1;j<=n;j++)
27             if(matrix[i][j])
28             {
29                 num[0][i]++;
30                 num[1][j]++;
31             }
32 }
33 
34 int main()
35 {
36     int t;
37     scanf("%d",&t);
38     while(t--)
39     {
40         int i,a,b;
41 
42         memset(matrix,0,sizeof(matrix));    //初始化
43         memset(num,0,sizeof(num));
44         flag=0;
45 
46         scanf("%d%d",&n,&m);
47         for(i=0;i<m;i++)    //預處理
48         {
49             scanf("%d%d",&a,&b);
50             matrix[a][b]=1;
51         }
52         floyd();
53         if(flag==0)    //輸出,當一個數比它大的和比它小的都小於n/2時,可視為該項為中間項
54         {
55             for(i=1;i<=n;i++)
56             {
57                 if(num[0][i]<=n/2&&num[1][i]<=n/2) printf("1");
58                 else printf("0");
59             }
60             printf("\n");
61         }
62         else
63         {
64             for(i=0;i<n;i++) printf("0");
65             printf("\n");
66         }
67     }
68     return 0;
69 }
ZOJ P4124

(用Floyd算法解此題算是一個比較巧妙的解法)

 

四、相關題目

 

1.例題 Luogu P2910

代碼:

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 int matrix[105][105],n;
 6 
 7 void floyd()
 8 {
 9     int i,j,k;
10     for(k=1;k<=n;k++)
11         for(i=1;i<=n;i++)
12             for(j=1;j<=n;j++)
13             {
14                 matrix[i][j]=min(matrix[i][j],matrix[i][k]+matrix[k][j]);
15             }
16 }
17 
18 
19 int main()
20 {
21     int i,j,m,order[10005]={0},ans=0;
22     scanf("%d%d",&n,&m);
23     for(i=0;i<m;i++) scanf("%d",&order[i]);
24     for(i=1;i<=n;i++)
25         for(j=1;j<=n;j++) scanf("%d",&matrix[i][j]);
26     floyd();
27     for(i=0;i<m-1;i++)
28         ans+=matrix[order[i]][order[i+1]];
29     printf("%d",ans);
30     return 0;
31 }
Luogu P2910

 

Author : Houge  Date : 2019.6.1

Update log : 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM