你的 Foyld 怎么只跑了一遍???

· · 生活·游记

(标题上放了个彩蛋,有没有人看出来了)

引言

众所周知,Floyd 可以简单的在 O(n^3) 的时间复杂度内求出图上任意两点之间的最短路长度,其实现形如:

for (int k = 1; k <= n; ++k)
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
            mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);

不过如果不小心写成了这个样子呢?

for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= n; ++j)
        for (int k = 1; k <= n; ++k)
            mp[i][j] = min(mp[i][j], mp[i][k] + mp[k][j]);

可以发现上面第二份代码其实类似于是对 mp 数组求自己和自己的 min-plus 矩阵乘法。当然因为直接在原数组上做了修改因此实际执行的并不是普通意义上计算两个矩阵 min-plus 卷积后得到的结果。

(并不)显然的是,上面的算法不一定能一次求出任意两点之间最短路的长度。但是一个非常有意思的问题是,如果将错误的 Floyd 算法重复执行若干次能不能得到正确的最短路结果?需要最少执行多少次?

有一个经典的结论是,Floyd 算法的三层循环无论如何修改顺序,只要重复执行上述流程三次一定可以得到正确的答案。更准确的说,六种循环顺序需要的最坏情况下最少的循环执行次数如下:

下面将会给出结论的证明。本文中假设图不存在负环,顶点的编号为 1,2,3,\ldots,n,初始化的距离为 mp_{i,i}=0 且每次执行都直接在原来的距离矩阵上做修改不重新初始化。对不可达的点对,距离记作 +\infin。

1. 为什么标准的 Floyd 只需要执行一轮?

这个是比较容易的。考虑 dp,记 f_{k,i,j} 表示从 i 到 j 只考虑编号不超过 k 的顶点作为中转点时的最短路长度。考虑是否经过顶点 k 则有转移 f_{k,i,j}=\min(f_{k-1,i,j},f_{k-1,i,k}+f_{k-1,k,j})。这个转移的正确性是显然的,滚动一下 k 的维度即可得到普通 Floyd 的实现。而对于固定的 k 交换 i,j 循环顺序显然不会影响正确性。因此 k,i,j 和 k,j,i 都只需要执行一轮即可求出正确的最短路长度。

2. i,k,j 为什么只需要执行两轮

先考虑这样一个关键的结论:对一条从 x 到 y 的最短路而言,若其经过的内部所有点的编号都小于 x,则第一轮处理完 i=x 后 mp_{x,y} 的值一定是正确的。这个结论可以归纳证明:对所有满足条件的最短路 x\to u_1\to u_2\to\ldots\to u_m\to y 其中 \forall u_i<x 从左向右扫描其内部顶点并只保留不断刷新前缀最大值的顶点,记为 v_1<v_2<\ldots<v_r<x 其中 v_1=u_1。观察相邻两个被保留的顶点 v_p,v_{p+1} 因为 v_{p+1} 是第一个超过 v_p 的顶点因此原来路径上这两个顶点之间的所有内部顶点都小于 v_p。最后一段 v_r\to y 同样也满足这个性质。因此根据对起点编号的归纳可知在处理第 x 行之前 mp_{v_p,v_{p+1}} 和 mp_{v_r,y} 的值都全部正确了,因为这些子路径的起点编号都小于 x。

现在考虑处理 i=x。因为 x\to v_1 本来就是一条边,而 k 从小到大枚举因此可以依次进行 x 从 v_1 到 v_2 再到 v_3 以知道 y 的整个流程。每一步需要的前缀距离都已经在前面的 k 中计算完成,而后缀距离则在处理第 x 行之前就已经算对了,因此最后可以得到正确的 mp_{x,y} 值。

现在考虑任意一条从 s 到 t 的最短路 s=u_0\to u_1\to \ldots\to u_m=t 从左往右扫描初始记录的最大编号为 s 之后只保留严格超过此前最大便好的内部顶点。设当前保留的顶点是 s=v_0<v_1<\ldots<v_r(t 不需要满足这个大小关系),则对任意相邻的两个博阿留顶点,原来路径中者之间的内部顶点编号都小于前一个保留的顶点编号,因此根据引理可知第一轮 Floyd 结束后可以将这些子路径分别压缩成一条权值正确的边。也就是说,第一轮 Floyd 之后一定存在一条等长的最短路,满足 s=v_0<v_1<\ldots<v_r\to t。除了 v_r,t 之间以外的最短路相邻结点部分编号一定是严格递增的。

然后考虑第二轮为什么一定可以求出答案,处理 i=s 时因为 k 是从小到大枚举的所以可以沿着刚才得到的路径依次转移 mp_{s,v_2}\leftarrow mp_{s,v_1}+mp_{v_1,v_2},mp_{s,v_3}\leftarrow mp_{s,v_2}+mp_{v_2,v_3},\ldots,mp_{s,t}\leftarrow mp_{s,v_r}+mp_{v_r,t}。这些边的距离在第一轮就已经算对了,因此第二轮一定能够计算出 s\to t 的最短路。因为 s,t 是任意的,可以得出结论,i,k,j 这个顺序重复执行两轮 Floyd 算法一定可以求出任意两个点之间最短路的长度。

然后证明一轮最短路不够是容易的,考虑一条 1\to 3\to 2\to 4 的有向图,图上每个边的边权都是 1,容易发现如果 i,k,j 顺序跑一轮 Floyd 无法正确求出 1,4 两个点之间的最短路长度。因此得出结论:i,k,j 需要重复执行两轮 Floyd 算法才能求出任意两个点之间的最短路长度。

### 3. $i,j,k$ 为什么只需要执行三轮 同样考虑另一个引理:对一条 $x$ 到 $y$ 的最短路,若两个端点的编号都大于所有内部顶点的编号,则第一轮处理完 $i=x$ 后 $mp_{x,y}$ 的值一定是正确的。同样考虑做归纳证明,对路径的长度进行归纳,若路径只有一条边则显然是成立的,否则若路径有两条边则直接枚举中转点就可以求出正确的最短距离。而对于有 $>2$ 条边的路径而言,设内部顶点中编号最大的点是 $p$ 则可以把路径拆分成 $x\to p\to y$ 这两个部分。因为有 $p<x\land p<y$ 且两端子路径的长度都比原路径要短,因此可以归纳:首先因为 $p<x$ 所以处理第 $x$ 行前第 $p$ 行的信息已经处理完成,因此 $mp_{p,y}$ 的值是正确的。然后根据归纳假设,处理第 $x$ 行时也可以求出正确的 $mp_{x,p}$ 的值,而因为 $p<y$ 所以枚举 $j$ 的时候必然需要先处理 $j=p$ 然后再处理 $j=y$,所以处理 $j=y,k=p$ 时可以进行操作 $mp_{x,y}\leftarrow\min(mp_{x,y},mp_{x,p}+mp_{p,y})$,而两段距离此时都已经计算正确,因此 $mp_{x,y}$ 也一定可以被计算正确。因此引理得证。 --- 先考虑第一次执行错误的 Floyd 做法。对任意一条最短路 $s=u_0\to u_1\to \ldots\to u_m=t$ 找到整条路经上编号最大的顶点 $p$ 并考虑分别处理其左右两侧的信息。对 $s\to p$ 这一段从左往右扫描,初始保留 $s$,之后只保留不断刷新前缀最大值的顶点;而对 $p\to t$ 这一段从右往左扫描,初始保留 $t$ 之后只保留不断刷新后缀最大值的顶点。这样做的意义是相邻两个保留顶点之间的所有顶点编号都小于这个两个端点。由前面的引理可知第一轮结束后这些子路径都可以被压缩成权值正确的边。因此这样可以得到一条等长度最短路,顶点的编号满足 $s=v_0<v_1<\ldots<v_h>v_{h+1}>\ldots>v_r=t$。也就是说,这样的最短路经过路径上的点编号必然是先严格递增再严格递减的,后面为了方便称这种路径为**单峰路径**。 然后考虑第二轮最短路。对刚才得到的单峰路径而言首先处理左半段 $s=v_0<v_1<\ldots<v_h$,因为 $j$ 从小到大枚举处理第 $s$ 行时,可以依次利用已经计算出的前缀信息得到 $mp_{s,v_2}\leftarrow mp_{s,v_1}+mp_{v_1,v_2},mp_{s,v_3}\leftarrow mp_{s,v_2}+mp_{v_2,v_3}$ 因此第二轮一定可以得到正确的 $mp_{s,v_h}$ 的值。右半边同理,处理完后一定可以得到正确的 $mp_{v_h,t}$ 的值。此时考虑合并左右两段路径,若当前三层循环的变量满足 $i=s,j=t,k=v_h$ 则执行 $mp_{s,t}\leftarrow\min(mp_{s,t},mp_{s,v_h}+mp_{v_h,t})$。因为右侧两个距离在第二轮 Floyd 结束的时候就已经被算对了因此这次转移必然可以得到 $s\to t$ 的最短路长度。因为 $s,t$ 任意,因此可以证明 $i,j,k$ 重复执行三轮必然可以求出图上任意两个点之间的最短路长度。 最后考虑为什么两轮 Floyd 不一定能求出来任意点对之间的最短路长度。这个东西同样考虑构造反例,对 $1\to 2\to 4\to 3\to 6\to 7\to 5$ 的有向图,所有边的边权都是 $1$,则考虑 $1,5$ 两个点之间的最短路长度,真正的最短路长度是 $mp_{1,5}=6$ 但是按照 $i,j,k$ 的循环顺序执行 Floyd 算法,前两轮都无法松弛到 $mp_{1,5}$ 也就是执行完两轮 Floyd 后有 $mp_{1,5}=+\infin$,因此至少需要三轮 $i,j,k$ 顺序的 Floyd 后才能求出图上任意两个点之间的最短路长度。 $j,i,k$ 顺序也是类似的证明过程,这里不再赘述。 ### 练习 [【模板】Floyd](https://www.luogu.com.cn/problem/B3647) [[PA 2019 Final] Floyd-Warshall](https://www.luogu.com.cn/problem/P11819) [[CSP-S 2026] 地铁 / subway](https://www.luogu.com.cn/problem/P17832) ### 参考文献 1. [Hide、Kumabe、Maehara (2019), Incorrect implementations of the Floyd–Warshall algorithm give correct solutions after three repeats](https://arxiv.org/abs/1904.01210). 2. [[CSP-S 2026] 地铁 / subway](https://www.luogu.com.cn/problem/P17832) 3. Luogu Academic 二代目群。