Floyd算法

1. 算法概述

Floyd算法(Floyd-Warshall算法)是一种解决所有节点对最短路径问题的动态规划算法。

核心思想

  • 通过三重循环逐步松弛所有可能的路径

  • 动态更新距离矩阵

  • "中介节点"思想:考虑通过某个中间节点k是否能缩短i到j的距离

特点

  • 可以处理负权边(但不能处理负权环)

  • 一次性求出所有节点之间的最短路径

  • 实现简单直观

2. 算法适用条件

✔ 带权有向图或无向图
✔ 可以处理负权边(但不能有负权环)
✔ 稠密图效果较好
✖ 不适用于大规模稀疏图(时间复杂度高)

3. 算法步骤

初始化阶段

// 初始化距离矩阵
for(int i = 1; i <= n; i++)
    for(int j = 1; j <= n; j++)
        if(i == j) d[i][j] = 0;      // 节点到自身的距离为0
        else d[i][j] = INF;          // 其他初始化为无穷大

// 输入边信息
while(m--) {
    cin >> a >> b >> w;
    d[a][b] = min(d[a][b], w);       // 处理重边
    // 如果是无向图:
    // d[b][a] = min(d[b][a], w);
}

动态规划过程

for(int k = 1; k <= n; k++)          // 中介节点
    for(int i = 1; i <= n; i++)      // 起点
        for(int j = 1; j <= n; j++)  // 终点
            d[i][j] = min(d[i][j], d[i][k] + d[k][j]);

4. 时间复杂度分析

项目复杂度说明
时间复杂度O(N³)三重循环
空间复杂度O(N²)需要存储距离矩阵

5. 代码模板(C++)

基础版本

const int N = 210, INF = 0x3f3f3f3f;
int d[N][N];
int n, m;

void floyd() {
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

int main() {
    cin >> n >> m;
    // 初始化
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            if(i == j) d[i][j] = 0;
            else d[i][j] = INF;
    
    // 读入边
    while(m--) {
        int a, b, w;
        cin >> a >> b >> w;
        d[a][b] = min(d[a][b], w);
        // 如果是无向图:
        // d[b][a] = min(d[b][a], w);
    }
    
    floyd();
    
    // 查询
    while(q--) {
        int a, b;
        cin >> a >> b;
        if(d[a][b] > INF / 2) cout << "impossible" << endl;
        else cout << d[a][b] << endl;
    }
    return 0;
}

记录路径版本

int path[N][N]; // 记录i到j的最短路径上j的前驱节点

void floyd_with_path() {
    // 初始化path
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            path[i][j] = j;  // 初始时直接连接
    
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                if(d[i][j] > d[i][k] + d[k][j]) {
                    d[i][j] = d[i][k] + d[k][j];
                    path[i][j] = path[i][k]; // 更新前驱
                }
}

void print_path(int i, int j) {
    if(i == j) {
        cout << i;
        return;
    }
    cout << i << "->";
    print_path(path[i][j], j);
}

6. 常见问题

Q1:Floyd算法为什么能处理负权边?

  • 算法会考虑所有可能的路径,包括经过负权边的路径

  • 通过动态规划逐步更新最短距离

Q2:如何判断图中是否存在负权环?

  • 检查距离矩阵中对角线元素(d[i][i])

  • 如果存在d[i][i] < 0,说明存在经过i的负权环

Q3:Floyd算法的k为什么要放在最外层?

  • 这是动态规划的阶段划分

  • 确保在考虑k作为中介节点时,i到k和k到j的最短路径已经考虑了所有1..k-1的中介节点

7. 例题

  1. LeetCode 1334. 阈值距离内邻居最少的城市

  2. ACWING 854 - Floyd求最短路

8. 优化技巧

  1. 对于稀疏图可以先进行节点重编号

  2. 使用位运算优化传递闭包计算

  3. 并行化最内层循环(现代CPU支持)

9. 声明

以上笔记仅为学习该算法的学习笔记,如有错误请指出

总结:Floyd算法是解决所有节点对最短路径问题的经典算法,虽然时间复杂度较高,但在稠密图和小规模图上非常实用,且实现简单直观。理解其动态规划思想对掌握图论算法有重要意义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值