深入理解Floyd算法思路

Floyd算法經典代碼只有5行(或者也可以寫成4行),思路聽起來也那麼“顯然”。

但大部分人對Floyd算法的理解僅僅限於它的實現是三層循環,k在最外層,i和j在內層不分順序,用路徑鬆弛來更新最短路徑。但其實大多數人都沒有弄清楚一個問題,爲什麼外層循環是遍歷k,而不是把k放到最內層呢,只是知道放在裏邊就不對了。當然,也有不少博客解釋這個問題,但寫的大多有些複雜,下面是我個人的理解。

首先,它是運用了動態規劃的思想來進行問題求解。動態規劃解題的關鍵在於找好子結構。Floyd構造的結構非常巧妙:找i和j之間通過編號不超過k(k從1到n)的節點的最短路徑(一定要注意,這裏是當前最短路徑,當k=n時達到最終最短路徑)。爲了便於說明,我們可以弄一個三維數組f[k][i][j]表示i和j之間可以通過編號不超過k的節點的“最短路徑”。對於k-1到k,只有兩種可能,經過編號爲k的點,要麼不能找到一條從i到j的更短路,此時有f[k][i][j] = f[k-1][i][j] ;要麼能找到,那這個最短路徑一定是d[i][k]+d[k][j],那麼就用這個較小的距離去更新d[i][j]。綜合以上兩種情況,f[k][i][j] = min(f[k-1][i][j] , f[k-1][i][k]+f[k-1][k][j])。

具體實現就比較簡單了,對任意兩點間的距離,我們初始化他們爲無窮大(這裏無窮大是一種形象的說法),然後用鬆弛技術進行距離的更新。然後用三層循環的話,最外層空間就可以省下來了,因爲f[k]只與f[k-1]有關。

好啦,下面給一個Floyad算法經典水題。

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N = 100;
int map[N][N],n,m;
int main()
{
	int x,y,z;
	scanf("%d%d",&n,&m);
	memset(map,0x3f,sizeof(map));
	/*for(int i=0;i<N;i++)
        for(int j=0;j<N;j++)
        printf("%d ",map[i][j]);*/
	for(int i=0;i<m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		map[x][y] = map[y][x] = z;
	}
	for(int k=1;k<=n;k++)
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(map[i][j] > map[i][k] + map[k][j])
				{
					map[i][j] = map[i][k] + map[k][j];
				}
			}
		}
	}
	while(scanf("%d%d",&x,&y))
	{
		printf("%d\n",map[x][y]);
	}
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章