AT_abc208_d 題解

Brilliant11001發表於2024-08-10

題目傳送門

做完這道題後感覺對 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;
}