最短路——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,永遠找不到最短路。其實如果一個圖中帶有“負權迴路”那麼這個圖則沒有最短路。
-
演算法的思路
通過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;
}