floyd算法的简单应用

floyd算法是用于求所有点对之间最短路的经典算法,但它的用途并不限于最短路。

例一、无向图的最小环问题:给定一张无向图,求经过至少三个点且长度最短的环的长度(点数n\leqslant 300)。

我做这题时并没有想到用floyd算法,导致了严重的后果。看来,凡是点数很少的最短路题,都不能忘了floyd算法啊!在floyd算法的运行过程中,我们先枚举必经点k,再枚举起点i和终点j,在三个点两两不同的情况下,求出的最短路不就会经过三个点了吗?在枚举到点k时,dis[i][j]中存储的是从点i到点j的,经过点k,且不经过超过k的点的最短路,那能不能由此求出从点i到点j的,经过点k,且不经过超过k的点的最小环?核心代码如下。

for(int k=1;k<=n;k++)
{
	for(int i=1;i<k;i++)//注意枚举范围,因为要保证i!=j!=k
		for(int j=1;j<i;j++)
			ans=min(ans,dis[i][j]+w[i][k]+w[k][j]);//更新最小环长度
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}

例二、给定一张无向图中所有点对的最短路,求边权之和的最小值,如不存在,输出-1

这道题虽然并没有直接运用到floyd算法,但还是需要在理解floyd算法更新最短路的方式的基础上进行思考。我们先考虑输出-1的情况,此时一定有点对之间的最短路矛盾。我们构造几组样例:dis[1][2]=1\ dis[2][3]=2\ dis[1][3]=4,因为dis[1][2]+dis[2][3]<dis[1][3],所以dis[1][3]应该被更新为dis[1][2]+dis[2][3],所以产生了矛盾;dis[1][2]=1\ dis[2][3]=2\ dis[1][3]=2,因为dis[1][2]+dis[2][3]>dis[1][3],所以dis[1][3]不需要被更新,不应输出-1。至此,我们解决了输出-1的问题:当存在点ijk满足dis[i][j]>dis[i][k]+dis[k][j],输出-1。现在考虑如何让边权之和最小。两点什么时候需要连边?当这两点间的最短路不能被任何一个点kk!=ik!=j)更新,即dis[i][j]<dis[i][k]+dis[k][j]。我们已经讨论了dis[i][j]>dis[i][k]+dis[k][j]dis[i][j]<dis[i][k]+dis[k][j]两种情况,那如果dis[i][j]=dis[i][k]+dis[k][j]怎么办?点i和点j之间可以连一条权值为dis[i][k]+dis[k][j]边,当然也可以不连,为保证边权之和最小,我们不应连边。核心代码如下。

memset(tmp,127,sizeof(tmp));
for(int i=1;i<=n;i++)
	for(int j=i+1;j<=n;j++)
	{
		for(int k=1;k<=n;k++)//k此时不需要放到最外层枚举
		{
			if(k==i||k==j) continue;
			if(dis[i][j]>dis[i][k]+dis[k][j]) return !puts("-1");
			tmp[i][j]=min(tmp[i][j],dis[i][k]+dis[k][j]);
			//tmp[i][j]表示i和j之间经过其它点的最短路
		}
		if(tmp[i][j]>dis[i][j]) ans+=dis[i][j];
	}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值