Floyd 最短路CSP-S

目录

💡 核心思想

Floyd 算法求全源最短路——任意两点之间的最短距离。核心是一个简单的三重循环:枚举中转点 $k$,对每对 $(i,j)$ 尝试通过 $k$ 中转是否更短。虽然 $O(n^3)$ 的复杂度看起来很大,但代码极其简洁,$n \le 500$ 时完全可用。

Floyd 的代码只有三行,但 k 必须在最外层循环!这个顺序不能改。原因是我们在考虑"只经过编号 $\le k$ 的节点中转"时的最短路,k 是逐步放宽的。如果把 k 放到内层,语义就完全不同了。

🎯 直觉理解

Floyd 就像"搭桥"。一开始每对城市之间只有直飞的航班。然后一个一个城市地"开放中转"——开放第1个城市作为中转站后,有些城市对就可能找到更便宜的路线了(经过第1个城市转机)。依次开放所有城市,最终得到所有城市对的最短路径。

📝 算法流程

  1. 初始化:dist[i][j] = 直接边的权,没有边则 INF
  2. 三重循环:k 在最外层
  3. 更新:$dist[i][j] = \min(dist[i][j], dist[i][k] + dist[k][j])$
  4. 可同时求传递闭包

$$dist[i][j] = \min_{k=1}^{n}(dist[i][j], dist[i][k] + dist[k][j])$$

📊 复杂度分析

指标复杂度
时间$O(n^3)$
空间$O(n^2)$

💻 参考实现(C++)

C++ (C++17)
#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, m; cin >> n >> m;
    const int INF = 1e9;
    vector<vector<int>> dist(n+1, vector<int>(n+1, INF));
    for (int i = 1; i <= n; i++) dist[i][i] = 0;
    for (int i = 0; i < m; i++) {
        int u, v, w; cin >> u >> v >> w;
        dist[u][v] = min(dist[u][v], w); // 处理重边
    }
    for (int k = 1; k <= n; k++)        // 中转点 k 必须在最外层!
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                if (dist[i][k] < INF && dist[k][j] < INF)
                    dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++)
            cout << dist[i][j] << " ";
        cout << endl;
    }
    return 0;
}

⚠️ 常见坑点

k 必须在最外层循环!这是最常见的错误

INF + INF 会溢出,要加判断

重边要取最小值

n > 500 时 Floyd 可能 TLE

📚 相关题目

题目来源难度备注
P1119 灾后重建洛谷CSP-SFloyd变形
P1364 医院设置洛谷CSP-JFloyd求最短路