有边数限制的最短路:Bellman-Ford 算法

有边数限制的最短路问题解析

问题背景

想象你是一位快递员,需要在复杂的城市路网中寻找最短路径。但老板给你一个特殊限制:最多只能经过k条街道 !同时,有些道路可能是单行道 (有向图),有些路段会倒贴你钱 (负权边),甚至可能存在永远绕不出去的怪圈(负权回路)。这就是我们要解决的有边数限制的最短路问题!

解决方案:Bellman-Ford 算法

算法核心思想

我们采用Bellman-Ford算法,其精妙之处在于:

  1. 分层更新:通过k次迭代,逐步构建"最多经过1条边"、"最多经过2条边"...直到"最多经过k条边"的最短路径
  2. 状态备份 :关键技巧!每次更新前复制当前状态,避免出现"用当前轮次更新的路径去更新其他路径"的更新串联问题
  3. 容忍负权:不同于其他算法,此方法能正确处理负权边(但会避开负权回路的陷阱)
python 复制代码
# 算法流程伪代码
def 有边数限制最短路():
    初始化距离数组: 起点为0,其他为正无穷
    for 迭代次数 in range(k):  # 限制k条边
        创建当前距离的备份副本
        for 每条边 in 所有边集合:
            用备份副本更新目标节点: 
                新距离 = min(当前距离, 备份的起点距离 + 边权)

为什么需要备份?

想象银行转账场景:没有备份时,同一轮中A转给B的钱,B可能立即转给C,导致实际上经过了两条边,却在一轮操作中完成。备份机制确保每次更新都基于上轮结算后的状态,严格保证边数限制!


完整代码实现

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

const int N = 510, M = 10010;  // 最大点数和边数
const int INF = 0x3f3f3f3f;    // 魔法数字:表示正无穷

int dist[N];      // 存储1号点到各点的最短距离
int backup[N];    // 防串联的备份数组
int n, m, k;      // 点数、边数、边数限制

struct Edge 
{
    int a, b, w;  // 起点、终点、边权
} edges[M];

void bellman_ford() 
{
    memset(dist, 0x3f, sizeof dist);  // 初始化距离为无穷大
    dist[1] = 0;  // 起点距离为0

    // 核心迭代:限制k条边就循环k次
    for (int i = 0; i < k; i++) {
        memcpy(backup, dist, sizeof dist);  // 关键备份!防串联
        
        // 遍历所有边进行松弛操作
        for (int j = 0; j < m; j++) {
            auto e = edges[j];
            // 使用备份更新,确保只增加一条边
            dist[e.b] = min(dist[e.b], backup[e.a] + e.w);
        }
    }
}

int main() 
{
    cin >> n >> m >> k;
    
    // 读入所有边
    for (int i = 0; i < m; i++) 
    {
        int a, b, w;
        cin >> a >> b >> w;
        edges[i] = {a, b, w};
    }

    bellman_ford();
    
    // 终点距离判断
    if (dist[n] > INF / 2) 
    {  // 考虑负权边影响
        cout << "impossible";
    } 
    else 
    {
        cout << dist[n];
    }
    
    return 0;
}

关键细节揭秘

  1. 备份的必要性

    去掉memcpy(backup, dist, sizeof dist)代码后,会出现路径边数失控。例如当k=1时,可能实际得到经过2条边的路径!

  2. INF/2的玄机

    由于负权边的存在,不可达点可能被更新为INF - 10000量级的值。我们通过dist[n] > INF/2判断,避免因负权边干扰可达性判断。

  3. 负权回路的处理

    虽然题目允许存在负权回路,但边数限制天然规避了无限绕圈的问题,算法仍能正确工作!


实战小贴士

  • 🚨 自环和重边无需特殊处理,算法天然兼容
  • ⏱ 时间复杂度:O(k×m)O(k \times m)O(k×m),完美匹配题目范围
  • 🔍 调试技巧:在k循环内打印dist数组,观察每轮变化
  • 💡 变式思考:如何记录具体路径?添加prev[]数组回溯即可!

挑战提示:尝试修改代码,当存在负权回路但不超过k条边时,如何检测并报警?试试看,你会有新发现!


算法内容来自AcWing算法基础课,感谢AcWing老师的详细讲解。

相关推荐
小程故事多_8014 小时前
从A2C、TRPO、PPO到GRPO,强化学习策略梯度算法完整演进与大模型落地实战解析
人工智能·算法
2601_9561219715 小时前
二分算法(知识点+题目)
c++·算法
星蓝_starblue16 小时前
零服务器、零数据库!开源growth-board,利用GitHub自动管理刷题/学习/求职全流程
服务器·数据库·程序人生·系统架构·node.js·github·改行学it
蒙奇·D·路飞-17 小时前
OpenClaw:开源的《合金弹头》精神续作与终极本地部署指南
c++·开源·mfc
AndrewHZ18 小时前
【LLM技术全景】阶段总结:技术原理篇核心知识回顾
人工智能·深度学习·算法·语言模型·大模型·llm·芯片开发
小星星闪亮登场19 小时前
2026萌新联赛第三场-- (郑州轻工业大学)
数据结构·c++·经验分享·算法·贪心算法·排序算法·深度优先
梓䈑19 小时前
【用 Vibe Coding 实现的 C++17 在线判题系统】前端开发 + Web 自动化测试
前端·c++·ai编程
冻柠檬飞冰走茶19 小时前
PTA基础编程题目集 7-8超速判断(C++语言实现)
开发语言·数据结构·c++·算法
玖玥拾20 小时前
LeetCode 88 合并两个有序数组
算法·leetcode
TunerT_TQ20 小时前
Valhalla 静态工程审阅 #018|MMCV 源码证据驱动评测【大厂开源基础设施特辑】
c++·开源·mfc·#计算机视觉·#商汤科技·#openmmlab·#静态源码审计