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

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

要将DataGridView的数据导出到Excel,并且实现滚动条显示进度的功能,你可以使用《DataGridView导出Excel并附滚动条进度功能》中提供的C#类。该类包含两个关键方法:`DataGridviewShowToExcel` 和 `DateGridViewCsvExcel`,分别用于处理导出操作和格式转换。 参考资源链接:[DataGridView导出Excel并附滚动条进度功能](https://wenku.csdn.net/doc/v9ejmv9tgr?spm=1055.2569.3001.10343) 首先,使用 `DataGridviewShowToExcel` 方法可以实现将DataGridView中的数据导出到Excel文件的功能。方法接受DataGridView对象和一个布尔值参数来决定是否显示Excel应用程序。导出过程中,如果DataGridView没有数据,方法会返回false;如果有数据,它会创建一个新的Excel工作簿,并将数据写入。写入过程中,列标题会被写入第一行,而单元格数据则根据其类型被适当处理后写入。如果指定显示Excel,用户可以看到进度和最终结果。 其次,`DateGridViewCsvExcel`方法可能用于将DataGridView数据转换为CSV格式并导出到Excel。该方法的实现细节未在提供的资料中给出,但可以从方法名推测,它可能利用了CSV格式的文本文件来实现数据的导出。 为了确保导出过程的顺利进行,你应该确保DataGridView中包含了要导出的数据,并且数据类型得到了正确的处理。在实际应用中,你可能还需要处理异常和错误,比如数据类型不匹配或者文件写入权限问题。此外,你还可以根据需要对进度条的显示进行定制,比如调整它的位置、大小或显示方式,以更好地适应你的应用程序界面。 通过上述方法,你将能够有效地将DataGridView中的数据导出到Excel文件,并提供用户友好的滚动条进度指示,使得数据处理更加直观和高效。如果你希望更深入地了解如何使用这些方法,或者在实际操作中遇到问题,可以参考《DataGridView导出Excel并附滚动条进度功能》中的详细讲解和示例代码。 参考资源链接:[DataGridView导出Excel并附滚动条进度功能](https://wenku.csdn.net/doc/v9ejmv9tgr?spm=1055.2569.3001.10343)
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值