Floyd_任意點之間的最短路徑演算法
一、演算法介紹:
Floyd–Warshall(簡稱Floyd演算法)是一種著名的解決任意兩點間的最短路徑(All Paris Shortest Paths,APSP)的演算法。從表面上粗看,Floyd演算法是一個非常簡單的三重迴圈,而且純粹的Floyd演算法的迴圈體內的語句也十分簡潔。Floyd演算法可以說是Warshall演算法的擴充套件,三個for迴圈就可以解決問題,所以它的時間複雜度為O(n^3)。從本質上說,Floyd是一個動態規劃(Dynamic Programming,DP)思想的體現,網上不少文章介紹得很籠統,沒有解釋清楚基本原理。故在此獻上我的想法。
二、Floyd演算法:
首先引入一個非常重要的概念:
我們假設Dis(k,i,j)代表著從源點i到終點j的一個最短路徑值,且k是該路徑中所有點編號的最大值 (而不僅僅只是經過和不經過的問題)。即i到j的路徑中所有點的編號都小於等於k。
有了這個概念之後,我們來看看是否能夠找出一個迭代關係。
一定的是,對於Dis(k,i,j)的路徑中,可能沒有包含編號為k的點,也可能包含了編號為k的點,利用這僅有的兩種情況可以定義他的迭代關係。
第一種情況下,由於沒有經過包含編號k的點,所以Dis(k,i,j) == Dis(k-1,i,j)。
第二種情況下,由於經過了編號為k的點,我們將路徑以k點分為兩部分Dis(k-1,i,k)和Dis(k-1,k,j),即等式“Dis(k-1,i,k)+Dis(k-1,k,j) == Dis(k,i,j)”
至此,我們在這僅有的兩種情況中進行選擇,路徑更小的就可以更新為源點i終點j且路徑中各個點的編號最大值為k的最短路徑了( - -真繞口)。即有Dis(k,i,j)=Min{ Dis(k-1,i,k)+Dis(k-1,k,j), Dis(k-1,i,j) }。
現在,我們有了迭代關係,缺少的只有初始條件與結束條件。
對於初始條件,i和j代表各個點,所以取值範圍只要在點的編號範圍內即可。由於k-1的存在,k的最小值為1。此時有Dis(1,i,j) = Min{ Dis(0,i,1) + Dis(0,1,j), Dis(0,i,j) }
對於結束條件,當k達到編號的最大值時,Dis(k,i,j)表示的就是經過點編號最大值為所有編號的最大值的路徑,此時也就是源點i到終點j的最大值了,因為k為最大值已經包含所有的點了。
這樣一來,我們就有了完整的遞推關係。要求任意點到任意點的最短距離就是當k值為最大值時候的Dis(k,i,j)了。
整個過程梳理一下:
1、用Dis[0,i,j]記錄每一對頂點的最短距離。
2、依次掃描每一個點,並以該頂點的編號值為k再遍歷所有每一對頂點Dis(k,i,j)的值,看看是否可用過該頂點的路徑讓這對頂點間的距離更小。
for(int k = 0; k < N; k++)
for(int i = 1; i <= N; i++)
for(int j = 1; j <= N; j++)
d[i][j] = min(d[i][j], d[i][k]+d[k][j]);
PS:1.整個演算法中k-1代表的不是數字意義上的-1而是編號的更小一位。如編號分別為(1,10,100,1000)當k代表編號1000時,k-1代表編號100。根據實際情況為準。
2.由於計算到Dis(k,i,j)時, 任意頂點間的路徑不大於k-1的值已經全部求出(k是遞增的),所以可以儲存該演算法的正確性。
三、例題:
http://poj.org/problem?id=2139
題意:奶牛們最近要拍電影了……(黑白電影麼)
1、若兩個的奶牛一起拍過電影,則他們之間的距離為1;
2、若兩隻奶牛a、b沒有一起拍過電影,但與另有一隻奶牛c都和他們拍過電影,則a、b的距離為2(通過c得到)。
求奶牛的與其他奶牛的距離的平均值的一百倍的整數。
所以我做為例題的題目都是基礎題,僅僅是做為演算法練手。。
六、Talk is cheap,show me the code(例題程式碼)
#include <iostream>
using namespace std;
#define INFINITY 301;
void Floyd(int **p, int N, int **path = NULL)
{
int i, j, k;
for (k = 0; k < N; k++)
{
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
{
if (i != j && p[i][j] > p[i][k] + p[k][j])
{
p[i][j] = p[i][k] + p[k][j];
#ifdef PATH
path[i][j] = path[k][j];
#endif
}
}
}
}
}
int main(void)
{
int N, M;
cin >> N >> M;
int **Cows = new int*[N];
int i, j;
for (i = 0; i < N; i++)
{
Cows[i] = new int[N];
for (j = 0; j < N; j++)
{
if (i == j)
Cows[i][j] = 0;
else
Cows[i][j] = INFINITY;
}
}
int *input;
while (M--)
{
int num;
cin >> num;
input = new int[num];
for (i = 0; i < num; i++)
cin >> input[i];
for (i = 0; i < num; i++)
for (j = i + 1; j < num; j++)
Cows[input[i] - 1][input[j] - 1] = Cows[input[j] - 1][input[i] - 1] = 1;
}
//Floyd
Floyd(Cows, N);
/*
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
cout << Cows[i][j]<<" ";
cout << endl;
}*/
int min = N * INFINITY;
for (i = 0; i < N; i++)
{
int sum = 0;
for (j = 0; j < N; j++)
if (i != j)
sum += Cows[i][j];
min = sum < min ? sum : min;
}
cout << min * 100 / (N - 1) << endl;
system("PAUSE");
return 0;
}