Floyd_Warshall Algorithm

10 篇文章 0 订阅
5 篇文章 0 订阅

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

#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]采取同样的分析方法.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值