1. 程式人生 > >最短路——floyd演算法

最短路——floyd演算法

1. Floyd演算法的介紹

  • 演算法的特點:
    弗洛伊德演算法是解決任意兩點間的最短路徑的一種演算法,可以正確處理有向圖或有向圖或負權(但不可存在負權迴路)的最短路徑問題,同時也被用於計算有向圖的傳遞閉包。Floyd-Warshall演算法的時間複雜度為O(N3),空間複雜度為O(N2)。

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

  • 081030elthvel6et6k886y.png

  • 演算法的思路

通過Floyd計算圖G=(V,E)中各個頂點的最短路徑時,需要引入兩個矩陣,矩陣S中的元素a[i][j]表示頂點i(第i個頂點)到頂點j(第j個頂點)的距離。矩陣P中的元素b[i][j],表示頂點i到頂點j經過了b[i][j]記錄的值所表示的頂點(即i-->b[i][j]-->j)。

假設圖G中頂點個數為N,則需要對矩陣D和矩陣P進行N次更新。初始時,矩陣D中頂點a[i][j]的距離為頂點i到頂點j的權值;如果i和j不相鄰,則a[i][j]=∞,矩陣P的值為頂點b[i][j]的j的值。 接下來開始,對矩陣D進行N次更新。第1次更新時,如果”a[i][j]的距離” > “a[i][0]+a[0][j]”(a[i][0]+a[0][j]表示”i與j之間經過第1個頂點的距離”),則更新a[i][j]為”a[i][0]+a[0][j]”,更新b[i][j]=b[i][0]。 同理,第k次更新時,如果”a[i][j]的距離” > “a[i][k-1]+a[k-1][j]”,則更新a[i][j]為”a[i][k-1]+a[k-1][j]”,b[i][j]=b[i][k-1]。更新N次之後,操作完成!

2. Floyd演算法的例項過程

上面,我們已經介紹了演算法的思路,如果,你覺得還是不理解,那麼通過一個實際的例子,把演算法的過程過一遍,你就明白了,如下圖,我們求下圖的每個點對之間的最短路徑的過程如下:

這裡寫圖片描述

第一步,我們先初始化兩個矩陣,得到下圖兩個矩陣:
這裡寫圖片描述

這裡寫圖片描述

第二步,以v1為中階,更新兩個矩陣:
發現,a[1][0]+a[0][6] < a[1][6] 和a[6][0]+a[0][1] < a[6][1],所以我們只需要矩陣D和矩陣P,結果如下:

這裡寫圖片描述

這裡寫圖片描述

通過矩陣P,我發現v2–v7的最短路徑是:v2–v1–v7

第三步:以v2作為中介,來更新我們的兩個矩陣,使用同樣的原理,掃描整個矩陣,得到如下圖的結果:

這裡寫圖片描述
這裡寫圖片描述

OK,到這裡我們也就應該明白Floyd演算法是如何工作的了,他每次都會選擇一箇中介點,然後,遍歷整個矩陣,查詢需要更新的值,下面還剩下五步,就不繼續演示下去了,理解了方法,我們就可以寫程式碼了。

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define inf 0x3f3f3f3f
int maze[110][110];
int main()
{
   int i,j,n;
   while(~scanf("%d",&n)&&n)
   {
      for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
          maze[i][j]=(i==j?0:inf);
      for(i=1;i<=n;i++)
      {
         int a,b,m;
         scanf("%d",&m);
         for(j=0;j<m;j++){
             scanf("%d%d",&a,&b);
             maze[i][a]=b;
          }
      }

      //floyd 拿一個點去更新所有兩點之間的最短距離,直至所有的點都用完
      for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
          for(int k=1;k<=n;k++)
            maze[j][k]=min(maze[j][k],maze[j][i]+maze[i][k]);
      int t,u,ans,minn=inf;
      for(i=1;i<=n;i++)
      {
         ans=0;
         for(j=1;j<=n;j++)
            if(maze[i][j]>ans)
               ans=max(ans,maze[i][j]);
         if(minn>ans)
         {
            minn=ans;
            u=i;
         }
      }
      if(minn==inf)
         printf("disjoint\n");
      else
         printf("%d %d\n",u,minn);
   }
   return 0;
}