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;
}