算法是用于求所有点对之间最短路的经典算法,但它的用途并不限于最短路。
例一、无向图的最小环问题:给定一张无向图,求经过至少三个点且长度最短的环的长度(点数)。
我做这题时并没有想到用算法,导致了严重的后果。看来,凡是点数很少的最短路题,都不能忘了
算法啊!在
算法的运行过程中,我们先枚举必经点
,再枚举起点
和终点
,在三个点两两不同的情况下,求出的最短路不就会经过三个点了吗?在枚举到点
时,
中存储的是从点
到点
的,经过点
,且不经过超过
的点的最短路,那能不能由此求出从点
到点
的,经过点
,且不经过超过
的点的最小环?核心代码如下。
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]);
}
例二、给定一张无向图中所有点对的最短路,求边权之和的最小值,如不存在,输出。
这道题虽然并没有直接运用到算法,但还是需要在理解
算法更新最短路的方式的基础上进行思考。我们先考虑输出
的情况,此时一定有点对之间的最短路矛盾。我们构造几组样例:
,因为
,所以
应该被更新为
,所以产生了矛盾;
,因为
,所以
不需要被更新,不应输出
。至此,我们解决了输出
的问题:当存在点
,
,
满足
,输出
。现在考虑如何让边权之和最小。两点什么时候需要连边?当这两点间的最短路不能被任何一个点
(
且
)更新,即
。我们已经讨论了
和
两种情况,那如果
怎么办?点
和点
之间可以连一条权值为
边,当然也可以不连,为保证边权之和最小,我们不应连边。核心代码如下。
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];
}