1.Floyd

能解决没有负环的图上的多源多汇的最短/最长路问题

修改之后能用来计算连通性(传递闭包),找无向图最小环和检测负环

令 $ f_{k,i,j} $ 表示从 $ i $ 号结点到 $ j $ 号结点只经过 $ i $ , $ j $ 和 $ 1 $ 到 $ k $ 号结点的最短路径

初始状态 $$ \begin{cases} f_{0,i,i}=0\\ f_{0,i,j}=w&如果i到j有一条权值为w的边(重边就选最小的)\\ f_{0,i,j}=+\infty&如果i到j没有边 \end{cases} $$

转移方程 $$ f_{k,i,j}=\min\{f_{k-1,i,j},f_{k-1,i,k}+f_{k-1,k,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][j]);
        }
    }
}

显然最长路就是把 min 变成 max

2.倍增Floyd

用来解决限定步数的最短/最长路问题

定义矩阵运算:$ (A*B)_{i,j}=\min\limits_{1 \leq k \leq n}\{A_{i,k}+B_{k,j}\} $

单位元是除了对角线上是 $ 0 $ 其余位置都是 $\infty$ 的矩阵

可以证明这个运算是满足结合律的

令 $ F^p $ 表示 $ \underbrace{F*F* \dots *F}_{p\text{个F}} $ (因为满足结合律所以无论怎么加括号结果都是一样的)

若 $ F $ 是邻接矩阵,则 $ (F^p)_{i,j} $ 表示从结点 $ i $ 到结点 $ j $ 恰好 走 $ p $ 步的最短路径

可以用快速幂计算 $ F^p $

最长路就是把 min 变成 max,单位元变成除了对角线上是 $ 0 $ 其余位置都是 $-\infty$ 的矩阵

代码:

// f[p][i][j]表示从i到j恰好走2^p步的最短路
// 初值:f[0][i][j]=infinity, 如果(i,j)没有边,否则f[0][i][j]=w(i,j)
for (int p = 1; (1 << p) <= nStep; p++) {
    // 里面三层循环可以任意交换顺序
    for (int k = 1; k <= nV; k++) {
        for (int i = 1; i <= nV; i++) {
            for (int j = 1; j <= nV; j++){
                f[p][i][j] = min(f[p][i][j], f[p-1][i][k] + f[p-1][k][j]);
            }
        }
    }
}

// 计算走恰好nStep的最短路,结果存在G数组里面
memset(G, 0x3f, sizeof(G));
for (int i = 1; i <= nV; i++) {
    G[i][i]=0;
}

for (int p = 0; (1<<p) <= nStep; p++) { // 和快速幂一样
    if (((1<<p) & nStep) == 0) {
        continue;
    }
    memset(F, 0x3f, sizeof(F));
    for (int k = 1; k <= nV; k++) {
        for (int i = 1; i <= nV; i++) {
            for (int j = 1; j <= nV; j++) {
                F[i][j] = min(F[i][j], G[i][k] + f[p][k][j]);
            }
        }
    }
    memcpy(G, F, sizeof(F));
}