最短路徑——Floyd算法(含證明)


通過dij,ford,spfa等算法可以快速的得到單源點的最短路徑,如果想要得到圖中任意兩點之間的最短路徑,當然可以選擇做n遍的dij或是ford,但還有一個思維量較小的選擇,就是floyd算法。


多源最短路徑算法

Floyd算法

思維

先直觀做個思考,一張圖,任意兩個點,已知兩點間的路徑權值,如果在圖中能夠找到一個點插入到這兩點的路徑之中,使得構成的路徑權值小於之前的路徑權值。就可以認為這條路比之前的路更短,這個點是屬於兩點間最短路徑的。由此可以得到一個遞推公式:

\[e[u][v]=min(e[u][v],e[u][k]+e[k][v]) \]

問題就轉換成了e[u][k],e[k][v]最短路徑的問題,這就是一種動態規划。

只要把圖枚舉一遍就可以得到所有點之間的最短路徑:

for (int k = 1; k <= n; k++)
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				e[i][j] = min(e[i][j], e[i][k] + e[k][j]);

說到動態規范,總會感嘆遞推式的優美,但是有沒有想過,為什么k循環在最外層,而且由於e[i][k],e[k][j]也是在不斷更新,而不是恆定的最小值,所以如何保證算法在最后一次松弛更新的時候,e[i][k],e[k][j]一定是最小的。

我們用歸納法(一生之敵)做一次證明:

  • 不妨做個命題:假設任意兩點i和j之間的路徑上可選擇經過的結點集合中,座號最大的是x,當k=x的時候,d[i][j]得到最小值。
  • 當i,j兩點之間路徑可選擇經過結點僅有一個點時候,命題是成立的。
  • 設i-x中座號最大的為x1,x-j中座號最大的為x2
  • 顯然x>x1,x>x2
  • 假設此時命題成立,則k=x1時,d[i][x]最小,k=x2時,d[x][j]最小。
  • 由此可以得到k=x的時候d[i][x]+d[x][j]已經是最小了,那么e[i][j]=min(e[i][j],e[i][x]+e[x][j])必然可以得到最小值。

由此可以證明在k循環完后就能夠得到最小的。當然也可以畫張圖直觀的感受一下。


代碼實現

#include<iostream>
#include<algorithm>
using namespace std;

const int MAX = 1000;

int e[MAX][MAX];
int n, m;

void Floyd() {
	for (int k = 1; k <= n; k++)
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				e[i][j] = min(e[i][j], e[i][k] + e[k][j]);
}

int main() {
	cin >> n >> m;
	const int inf = 100000;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			if (i == j) e[i][j] = 0;
			else e[i][j] = inf;
	int u, v, w;
	for (int i = 0; i < m; i++) {
		cin >> u >> v >> w;
		e[u][v] = w;
		e[v][u] = w;
	}
	Floyd();
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			cout << e[i][j] << " ";
	return 0;
}


免責聲明!

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



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