AT_abc208_d 题解

题目传送门

做完这道题后感觉对 Floyd 的理解更深了。

根据题面要求,设 \(f(k, i, j)\) 表示从 \(i\)\(j\) 的所有只经过 \(1\sim k\) 的点的所有路径的最短距离

很明显 \(k\) 那一维是阶段,因为它描述了从 \(i\)\(j\) 路径中的不同点,而我们就是根据这一条件来划分集合,这也是为什么 \(k\) 必须放在最外层循环。

所以状态转移方程为:

\[f(k, i, j) = \min\limits_{1\le i,j\le n}\{f(k - 1, i, k) + f(k - 1, k, j)\} \]

由于第 \(k\) 层只会用到第 \(k - 1\) 层的状态,所以可以加滚动数组优化。

和背包的状态转移方程就相似,还可以直接将这一维省去,因为在外层循环到 \(k\) 时,\(f(i, k)\) 恰好等于 \(f(k - 1, i, k)\)\(f(k, j)\) 同理。

再特判一个走不到的情况,即 \(f(k, i, j) = \infty\),然后全加起来就好了。

时间复杂度为 \(O(n^3)\)

\(\texttt{Code:}\)

#include <iostream>

using namespace std;

const int N = 410, inf = 0x3f3f3f3f;
int n, m;
int g[N][N];
long long res;

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            if(i != j) g[i][j] = inf;
    int a, b, w;
    for(int i = 1; i <= m; i++) {
        scanf("%d%d%d", &a, &b, &w);
        g[a][b] = min(g[a][b], w); //防重边
    }
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++) {
                g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
                if(g[i][j] < inf) res += g[i][j];
            }
    printf("%lld", res);

    return 0;
}
posted @ 2024-08-10 09:04  Brilliant11001  阅读(10)  评论(0编辑  收藏  举报