本算法是对算法导论上相关章节伪代码的实现:
#include<iostream>
#include<fstream>
#include<algorithm>
using namespace std;
#define MAX 32767
ifstream 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";
else
cout<<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 32767
int 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]采取同样的分析方法.