图论最短路 之 弗洛伊德Floyd(详细分析)

什么是Floyd算法

佛洛伊德是最简单的最短路径算法,可以计算图中任意两点间的最短路径。时间复杂度为O(N3),适用于出现负边权的情况。

怎么用代码实现?

算法描述:
( a )初始化:点u、v如果有边相连,则dis[u][v]=w[u][v]
如果不相连,则dis[u][v]=0x3f
( b )模板:

for(k=1;k<=n;k++){
	for(i=1;i<=n;i++){
		for(j=1;j<=n;j++){
			if(dis[i][j]>dis[i][k]+dis[k][j])
			  	dis[i][j]=dis[i][k]+dis[k][j];
		}
	}
}
	

( c )算法结束:dis[i][j]得出的就是任意起点i到任意终点j的最短路径。

疑问:为什么枚举中间点的循环k要放在最外层?

可以从一定不经过k点与一定经过k点的三维数组比较中推导出来



使用Floyd输出最短路径序列

Floyd算法输出路径也是采用记录前驱点的方式。因为floyd是计算任意两点间最短路径的算法,dis[i][j]记录从i到j的最短路径值。故我们定义pre[i][j]为一个二维数组,记录从i到j的最短路径中,j的前驱点是哪一个。递归还原路径。

初始化pre[N][N]为0,输入相连边时,重置相连边尾结点的前驱
若有无向边:pre[a][b]=a; pre[b][a]=b;

更新若floyd最短路有更新,那么pre[i][j]=pre[k][j];(这能不能直接赋值k的值?)

递归输出指两点s,e的最短路,先输出起点s,再将终点e放入递归,输出s+1~e的所有点。

void print(int x){
	if(pre[s][x]==0) return;
	print(pre[s][x]);
	printf(->%d”,x);
}


例题板题


① 最短路径问题

平面上有n个点(n<=100),每个点的坐标均在-10000~10000之间。其中的一些点之间有连线。若有连线,则表示可从一个点到达另一个点,即两点间有通路,通路的距离为两点间的直线距离。现在的任务是找出从一点到另一点之间的最短路径。

输入格式
第1行:1个整数n
第2…n+1行:每行2个整数x和y,描述了一个点的坐标
第n+2行:1个整数m,表示图中连线的数量
接下来有m行,每行2个整数i和j,表示第i个点和第j个点之间有连线
最后1行:2个整数s和t,分别表示源点和目标点


输出格式
第1行:1个浮点数,表示从s到t的最短路径长度,保留2位小数(如果到达不了,输出极大值0x7f对应的double类型的值)

样例

样例输入
5
0 0
2 0
2 2
0 2
3 1
5
1 2
1 3
1 4
2 5
3 5
1 5
样例输出
3.41

这一道十分水的题,就是Floyd的板题

代码

#include
#include
#include
#include
using namespace std;
const int M=105;
double G[M][M];
int xi[M],yi[M];
int main(){
	memset(G,0x7f,sizeof(G));
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d %d",&xi[i],&yi[i]);
	}
	int m;
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		int s,t;
		scanf("%d %d",&s,&t);
		G[s][t]=sqrt((xi[s]-xi[t])*(xi[s]-xi[t])*1.0+(yi[s]-yi[t])*(yi[s]-yi[t])*1.0)*1.0;
		G[t][s]=G[s][t];
	}
	int s,t;
	scanf("%d %d",&s,&t);
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				G[i][j]=min(G[i][j],G[i][k]+G[k][j]);
			}
		}
	}
	printf("%.2lf",G[s][t]);
}

② 跑步

小明作为一名刚学习信息学的蒟蒻(即魔芋[Talloran注]),一开始便感受到了这个领域深深的恶意.他决定每天下午外出跑步,以达到放松心情的目的.

小明生活的城市被分为n个地区,每两个地区间都可能存在一条双向联通的道路.而这些道路总共有k条.作为一名蒟蒻,他有着严重的强迫症,

因此,若两个地区间有多条直接联通道路,他会毫不犹豫地选择最长的那一条.
小明选择了作为神犇的你,帮助他求出在将来x天,从a区域到b区域的最短路径.

输入格式
第一行3个数字,分别是n,k,x.n表示小明所在城市中地区的个数,k表示地区间道路的总数,x表示小明查询的天数.
接下来k行,每行有3个数(可能为小数,不要说我没提醒你用某数据类型):c,d,e,表示从c到d直接联通的道路的长度为e
接下来x行,每行两个整数:a,b,表示小明今天查询的从a到b的最短路径(如果答案小数部分不为0,保留1位小数,否则输出整数).

输出格式
输出共x行,表示当天小明查询的最短路径

样例

输入:
5 7 3
1 2 6
2 3 10
3 4 5
4 5 4
5 1 2
2 5 8
1 4 7
1 4
1 3
3 5
输出:
6
11
9

代码

#include
#include
#include
using namespace std;
const int M=505;
double G[M][M];
int main(){
	for(int i = 0;i < M;i ++)
	for(int j = 0;j < M;j ++)
	G[i][j] = 0x7f7f7f7f * 1.0;
	int n,m,k;
	scanf("%d %d %d",&n,&m,&k);
	for(int i=1;i<=m;i++){
		double w;
		int s,t;
		scanf("%d %d %lf",&s,&t,&w);
		if(G[s][t] != 0x7f7f7f7f)
		G[s][t]=max(G[s][t],w);
		else
		G[s][t]=w;
		G[t][s]=G[s][t];
	}
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(G[i][j]>G[i][k]+G[k][j]){
					G[i][j]=G[i][k]+G[k][j];
				}
			}
		}
	}
	for(int i=1;i<=k;i++){
		int s,t;
		scanf("%d %d",&s,&t);
		printf("%g\n",G[s][t]);
	}
	return 0;
}

你可能感兴趣的:(图论,最短路,图论,c++)