floyd——记录路径,原理详解。

前言:floyd 算法求最短路(边权可为负)很优美,四行代码就搞定了。今天做了一个题,可以用 floyd 做,但是要最短路的路径。在网上搜了一阵,代码到是有,但是没有解释,为何是这样的?于是,手推了一遍,写了这篇博客。


Floyd 如何记录最短路的路径并输出呢?

不像 dijkstra 和 spfa,是一个点一个点加进去的,直接 pre 数组往前倒,倒至起点就行了。floyd 是基于动态规划,这怎么记录路径呢?

开一个 pass数组,pass[i][j] 表示:更新从 i 到 j 的最短路径时,经过的一个中转点。

void floyd()
{
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				if(dist[i][j]>dist[i][k]+dist[k][j]){
					dist[i][j]=dist[i][k]+dist[k][j];
					pass[i][j]=k; 
				}
}

在这里插入图片描述在这个图中,很容易看出,从 1 到 6 之间的最短路径是标红的那几条边。

二重循环所有点,我们输出 pass 数组:

0 0 0 3 4 5
0 0 0 0 4 5
0 0 0 0 4 5
0 0 0 0 0 5
0 0 0 0 0 0
0 0 0 0 0 0

可以看出,pass[1][6] 是 5,pass[1][5] 是 4,pass[1][4] 是 3。那么,最终 pass[i][j] 中存的就是从i到j的最短路径中的最后一个点。

而我们最终输出路径的思路就是,不断分段最短路径! 最后输出所有的点。

原理:由 i 到 j 的最短路径中的一点 k,将最短路径分段为从 i 到 k 的最短路径 和 从 k 到 j 的最短路径,最短路径就为从i到k的最短路径+从k到j的最短路径,一直分段,直到分到 i 和 j 为同一点,停止

可能现在你有些迷糊,我们直接看代码吧!

//pass[i][j]:从i到j最短路径中经过的一点k 
void print(int i,int j)
{
	if(i==j) return; //分段到同一点,停止 
	if(pass[i][j]==0) cout<<i<<" "<<j<<endl;//i和j直接相连,则就是最短路径中的边  (i到j最短路径不经过任何点) 
	else{
		print(i,pass[i][j]); //分段到从i到k的最短路径,
		//输出从i到k最短路径中的所有点(一定都在从i到j的最短路径中) 
		print(pass[i][j],j); //分段到从k到j的最短路径,
		//输出从j到k最短路径中的所有的点
	}
}

就是一个递归的过程。

我们用上面的图模拟一下:

  • 首先,从 1 到 6 的最短路径,pass[1][6]中存的是:该最短路径中的最后一个节点 5,即,k = 5。

  • 那么,递归(分段) 到,从 1到5的最短路 和 从5到6的最短路。
    1、从1到5的最短路,pass[1][5]=4,则又分段为从1到4的最短路和从4到5的最短路。
    2、从1到4的最短路,pass[1][4]=3,则又分段为从1到3的最短路和从3到4的最短路。
    3、pass[1][3]=0!如图,1和3直接相连!那么1和3都是最短路中的点,输出就行了!

  • 回溯:
    1、从3到4的最短路,pass[3][4]=0!3和4直接相连,3和4都是最短路中的点,输出!
    2、从4到5的最短路,pass[4][5]=0!4和5直接相连,4和5都是最短路中的点,输出!
    3、从5到6的最短路,pass[5][6]=0!5和6直接相连,5和6都是最短路中的点,输出!

所以,最终输出的就是:

1 3
3 4
4 5
5 6

依次连接就是从1到6的最短路径了!

总体代码:
void floyd()
{
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				if(dist[i][j]>dist[i][k]+dist[k][j]){
					dist[i][j]=dist[i][k]+dist[k][j];
					pass[i][j]=k;
				}
}
void print(int i,int j)
{
	if(i==j) return; 
	if(pass[i][j]==0) cout<<i<<" "<<j<<endl;
	else{
		print(i,pass[i][j]);
		print(pass[i][j],j); 
	}
}
int main(){
	···		//一顿初始化,输入数据
	
	floyd();
	print(1,n); //输出从1到n的最短路径中的所有点(边) 
	
	return 0;
} 

最后,附上我今天做的这道题:RoadBlock S

如果哪里不对或者不明白的地方,欢迎留言评论呀~

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值