Floyd_Warshall Algorithm

来源:互联网 发布:vs2015 mysql 编辑:程序博客网 时间:2024/06/05 21:09

本算法是对算法导论上相关章节伪代码的实现:

#include<iostream>#include<fstream>#include<algorithm>using namespace std;#define MAX 32767ifstream fin("data.in");int N,M;int u,v,l;int D[1000][1000],p[1000][1000],tmpD[1000][1000],tmpP[1000][1000];void print(int (*arr)[1000]){for(int i=0;i<N;++i){for(int j=0;j<N;++j){if(arr[i][j]==MAX)cout<<"∞"<<"\t";elsecout<<arr[i][j]<<"\t";}cout<<endl;}}void FloydWarshall(){for(int k=0;k<N;++k){for(int i=0;i<N;++i){for(int j=0;j<N;++j){if(D[i][k]<MAX&&D[k][j]<MAX){if(D[i][j]>D[i][k]+D[k][j]){tmpD[i][j]=D[i][k]+D[k][j];tmpP[i][j]=p[k][j];}else{if(tmpD[i][j]>D[i][j]){tmpD[i][j]=D[i][j];tmpP[i][j]=p[i][j];}}}}}swap(D,tmpD);swap(p,tmpP);}}int main(){fin>>N>>M;for(int i=0;i<N;++i){for(int j=0;j<N;++j){D[i][j]=tmpD[i][j]=MAX;p[i][j]=tmpP[i][j]=-1;}D[i][i]=tmpD[i][i]=0;}for(int i=0;i<M;++i){fin>>u>>v>>l;D[u-1][v-1]=tmpD[u-1][v-1]=l;if(l!=MAX)p[u-1][v-1]=tmpP[u-1][v-1]=u;}FloydWarshall();print(D);cout<<"intermedia vertex:"<<endl;print(p);return 0;}

对于两个节点能否连通的代码的实现版:

#include<iostream>#include<fstream>#include<algorithm>using namespace std;ifstream fin("data1.in");#define MAX 32767int N,M;int u,v,l;int D[1000][1000],tmp[1000][1000];void TransitiveClosure(){for(int k=0;k<N;++k){for(int i=0;i<N;++i){for(int j=0;j<N;++j){tmp[i][j]=D[i][j]||(D[i][k]&&D[k][j])||tmp[i][j];}}swap(D,tmp);}}int main(){fin>>N>>M;for(int i=0;i<N;++i){for(int j=0;j<N;++j){D[i][j]=0;}D[i][i]=1;}for(int i=0;i<M;++i){fin>>u>>v>>l;D[u-1][v-1]=1;}TransitiveClosure();for(int i=0;i<N;++i){for(int j=0;j<N;++j)cout<<D[i][j]<<"\t";cout<<endl;}return 0;}


我在看的时候,我就想啊:为什么核心代码

for  k <----1  to n

do for i <--- 1 to n

do for j <--- 1 to n

do.......

可以写成这样的呢.

矩阵乘法的核心代码是很好懂的:

for i <--- 1 to n

do for j <--- 1 to n

do for k <--- 1 to n

对于每一对节点,判断k是不是它们的中间节点.

Floyd-Warshell算法把k遍历提到了最前面,以此减少了n次遍历.为什么可以这样呢?

前面的遍历是很好懂的.当D[i][j]>D[i][k]+D[k][j],更新D[i][j].反之就不更新.那么现在问题就来了,如果在后面的遍历中,我们更新了D[i][k],那么还要不要在更新D[i][j]呢.Floyd-Warshell算法告诉我们不需要的.那为什么不需要呢.这个最难懂了.

我对本问题进行了分析,导致D[i][j]不能更新的原因是:D[i][j]<D[i][k]+D[k][j].再后面的遍历中,如果k还能作为D[i][j]的中间节点的话,那么一定更新了D[i][k]或D[k][j],因为每一次只能更新一个,所以D[i][k]和D[k][j]一定在一时间只更新了一个,更新就是减小,那D[i][k]或D[k][j]减小了,为什么不要在更新D[i][j]呢?

我们假设找到了节点m,m对D[i][k]进行了更新,那m一定是i,k的中间节点,D[i][m]就是i,m 的最小距离了,如果不是的话,我们考虑极端情况,m=n-1;这样D[i][m]就是i,m的最小距离了.为什么不在需要k了呢?因为如果k能更新的话,那m也一定能更新.因为D[i][k]=D[i][m]+D[m][k]是i,k 间的最小距离,如果此时D[i][j]=D[i][k]+D[k][j]是i,j间的最小距离,那么一定可以找到D[i][j]=D[i][m]+D[m][j]是i,j间的最小距离.此时k是m,j的中间节点.同样可以考虑极端情况.这样m就代替了k对D[i][j]进行更新,而如果k能进行更新的话,那k一定能在D[i][m]中或D[m][j]中找到.

对D[k][j]采取同样的分析方法.

0 0
原创粉丝点击