bitset 优化 Floyd 传递闭包
Floyd 算法
用于求图中任意两点最短路算法。
Floyd 算法本质是 dp。设 \(i\to j\) 最短路为 \(f_{i,j}\),初始置 \(f_{i,j}\) 为 \(i\to j\) 的边权;若 \(i\to j\) 没有直接连边,置 \(f_{i,j}\) 为 \(+\infty\)。
枚举 \(k\),检查 \(i\to k\to j\) 是否比原有的路径长度短,即令
当 \(k\) 遍历过所有点,全局最短路就确定了。
for (int k = 1; k <= n; ++k)
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
f[i][j] = min(f[i][j], f[i][k] + f[k][i]);
注意 Floyd 算法的枚举顺序是 \((k,i,j)\),更改枚举顺序会导致算法出错。
值得一提,在这篇论文证明,对于 \((i,k,j)\) 的枚举顺序,算法最多跑 \(2\) 遍即可得到正确结果;对于 \((i,j,k)\) 的枚举顺序,算法最多跑 \(3\) 遍即可得到正确结果。
不过,对单个变量的枚举顺序显然没有要求,正序倒序乱序显然都可以。
特别地,假设枚举变量 \(k\) 已经遍历且仅遍历了 \(S\) 中的点,当前答案的意义就是:只经过 \(S\) 中的点作为“中继”,任意两点的最短路。
bitset 优化传递闭包
计算任意两点是否可达。
我们对 dp 数组的含义重新定义,初始若 \(i\to j\) 有边,置 \(f_{i,j}\) 为 \(1\),否者置为 \(0\),特别地,\(i=j\) 时应置为 \(1\)。
转移方程为:
考虑用 bitset
优化。
我们用 bitset
数组作为 dp 数组,\(f_i\) 是一个长 \(N\) 的 bitset
。
转移方程显然可以看成:
注意到若 \(f_{i,k}\) 为真,则需遍历所有 \(j\) 对 \(f_{i,j}\) 和 \(f_{k,j}\) 取或。
这个过程可以用 bitset
优化。
for (int k = 1; k <= n; ++k)
for (int i = 1; i <= n; ++i)
if (f[i][k]) f[i] |= f[k];
由于 bitset
实现上会压位,时间复杂度是 \(O\left(\dfrac{N^3}{w}\right)\),\(w\) 是计算机的位数,一般是 \(32\) 或 \(64\)。
开 O2 优化后,编译器可能会进行循环展开,这会使常数变为原来的 \(\dfrac18\sim \dfrac14\)。