Somaur

导航

【零零散散】Floyd算法

Floyd算法的正确性

Floyd算法是一种动态规划算法。常见的模板中,Floyd算法的正确性并不明显,因为常用模板里的是经过状态压缩过的版本,比如这种:

for(int k = 1; k <= n; ++k){
    // 枚举中转点 
    for(int i = 1; i <= n; ++i){
        if(i == k) continue;
        for(int j = i + 1; j <= n; ++j){
            if(j == k) continue;
            dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
        }
    }
}

这也使得直接拿着模板看会不太好理解,会产生奇奇怪怪的问题,比如

  • 为什么中转点要在最外层枚举
  • $dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j])$ 不一定能得到最短路,为什么是正确的
  • ……

要搞定这些问题,首先我们要看Floyd算法的状态代表什么。Floyd算法的状态 $f[i][j][k]$ 代表 “从 $i$ 到 $j$ 的 途中经过的点的最大编号为$k$的 最短的 路径”。因此 $f[i][j][n]$ 就可以表示 $i$ 到 $j$ 的最短路径。从而可以得到状态转移方程:

$$dis[i][j][k] = min(dis[i][j][k-1], dis[i][k][k-1] + dis[k][j][k-1])$$

每次状态转移,都是判断能否通过 $i \rightarrow k$ 和 $k \rightarrow j$ 两条路径拼出比 不含 $k$ 号点时的最短路 更短的一条路。作为一个动态规划,每次更新都是局部最优解。

Floyd算法求两点之间最短路的方案数

/* init: dis <- INF , c <- 0 */
/* edge: dis <- length , c <- 1 */

for(int k=1;k<=n;++k){
    for(int i=1;i<=n;++i){
        if(i==k)continue;
        for(int j=i+1;j<=n;++j){
            if(j==k)continue;
            if(dis[i][j]==dis[i][k]+dis[k][j]){
                c[j][i]+= c[i][k]*c[k][j];
                c[i][j]+= c[i][k]*c[k][j];
            }
            else if(dis[i][j]>dis[i][k]+dis[k][j]){
                dis[j][i]=dis[i][j] = dis[i][k]+dis[k][j];
                
                c[j][i]=c[i][j] = c[i][k]*c[k][j];
            }
        }
    }
}

也是动态规划,因为使用的$k$的意义和Floyd相同,所以可以写在一起。

posted on 2021-11-15 16:30  Somaur  阅读(38)  评论(0编辑  收藏  举报