算法是用于求所有点对之间最短路的经典算法,但它的用途并不限于最短路。
例一、无向图的最小环问题:给定一张无向图,求经过至少三个点且长度最短的环的长度(点数)。
我做这题时并没有想到用算法,导致了严重的后果。看来,凡是点数很少的最短路题,都不能忘了算法啊!在算法的运行过程中,我们先枚举必经点,再枚举起点和终点,在三个点两两不同的情况下,求出的最短路不就会经过三个点了吗?在枚举到点时,中存储的是从点到点的,经过点,且不经过超过的点的最短路,那能不能由此求出从点到点的,经过点,且不经过超过的点的最小环?核心代码如下。
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];
}