我这两天好像和这些基础算法干上了。。
不过最短路算法的确还是有非常多非常巧妙的应用让我不得不总结的! 知其然不知其所以然真的是一件很容易触发伤痛的事情啊!
而且其实NOIP的题中本来就不怎么出图论, 出了图论也就是最短路, 并查集, 树上倍增这几种了吧(还有拓扑), 最多是加一些乱七八糟的东西来扰人耳目。
而不被干扰的唯一方法就是认清事物的本质, 认清本质需要在更高的一个层面上进行总结, 这也是我日后学习任何算法是应该做的
一, Floyd
暴力而优美的算法,最经典的三个for!
Floyd算法的基本思想如下:从任意节点A到任意节点B的最短路径不外乎2种可能,1是直接从A到B,2是从A经过若干个节点X到B。所以,我们假设Dis(AB)为节点A到节点B的最短路径的距离,对于每一个节点X,我们检查Dis(AX) + Dis(XB) < Dis(AB)是否成立,如果成立,证明从A到X再到B的路径比A直接到B的路径短,我们便设置Dis(AB) = Dis(AX) + Dis(XB),这样一来,当我们遍历完所有节点X,Dis(AB)中记录的便是A到B的最短路径的距离。
for(int k = 1; k <= n; k ++)
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
if(dis[i][j] > dis[i][k] + dis[k][j])
dis[i][j] = dis[i][k] + dis[k][j];
值得注意的一点事k 的循环是要放在最外层的。
否则如下图A到B是找不到比9更短的路径的, 但事实上它是存在的。
Floyd找到A到B的最短路时, 需要在这之前找到A到C的最短路和B到C的最短路, 所以只按照i 到 j 的点的顺序循环是不可以的。
依次扫描每一点(k),并以该点作为中介点,计算出通过k点的其他任意两点(i,j)的最短距离,这就是floyd算法的精髓!同时也解释了为什么k点这个中介点要放在最外层循环的原因.
上面一行是我看到一个人的解释, 但感觉还是在用结论证明结论啊。。
我个人的理解是感觉外层的循环像是在求一个连通块, 每访问到一个点K就把所有和K有关的点并起来, 显然在一个连通块中所有度大于1的节点都被当成K访问过, 所以任意两点仅通过集合中节点的最短路是已知的。 那么如果图是连通的, 外层循环访问完所有点后, 原图中任意两点间的最短路就是已知的了!
不知道我这么说是否清楚。。
不过深入地思考下去实在是会觉得Floyd是简约而不简单的。
应用
Floyd最经典的应用就是求最小环了吧
for(int k = 1; k <= n; k ++){
for(int i = 1; i <= k; i ++)
for(int j = 1; j <= k; j ++)
mincircle = min(mincircle, dis[i][k] + dis[k][j] + lu[i][j]);
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
if(dis[i][k] + dis[k][j] < dis[i][j])
dis[i][j] = dis[i][k] + dis[k][j];
}
在第K层外循环中,