RainPPR 发表于 2022-11-29 22:09

单源最短路算法:Bellman-Ford 学习笔记

本帖最后由 RainPPR 于 2022-11-29 22:11 编辑

最近在学图论,整理的学习笔记,分享一下{:1_911:}
新手发帖,如有问题请大佬指出,勿喷,谢谢。

# 单源最短路问题:Bellman-Ford

百科:**贝尔曼-福特算法**(**Bellman-Ford**)是由**理查德·贝尔曼**(Richard Bellman)和**莱斯特·福特** 创立的。有时候这种算法也被称为 **Moore-Bellman-Ford** 算法,因为 **Edward F. Moore** 也为这个算法的发展做出了贡献。

## 特征

### 适用情况

单源最短路:从1号点到其他所有点的最短距离
存在负权边:边的权重(长度)可以为负数
特点:可以(通常用SPFA求)判断是否有负权回路(若有负权回路,最短路径长度为负无穷)

适合解决(与SPFA算法相比)“**不超过 k 条边**”一类问题

### 特征

时间复杂度:O(nm)

图的存储可以直接用 *struct* 自定义结构体(存储每一条边的起点、终点、长度)

## 思路

1. 迭代 n 次:`for i : 1 ~ n` 【第 i 次操作表示:不超过 i 条边的最短距离】

   1. 枚举所有边:`for : ` ![](https://latex.codecogs.com/svg.image?a%20\overset{w}{\longrightarrow}%20b) 【松弛操作】

   2. `dist = min(dist, dist + w);`

## 证明

证明:`dist ≤ dist + w`(三角不等式)

## 经典应用:有边数限制的最短路

>
>给定一个 n 个点 m 条边的有向图,图中可能存在**重边和自环**, **边权可能为负数**。
>请你求出从 1 号点到 n 号点的最多经过 k 条边的最短距离,如果无法从 1 号点走到 n 号点,输出 `impossible`。
>注意:图中可能**存在负权回路** 。
>

#### 详解见代码注释

### 代码

```cpp
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 510, M = 10010;

// 返回最大值
inline int MAX(int a, int b)
{
      return (a > b) ? a : b;
}
// 返回最小值
inline int MIN(int a, int b)
{
      return (a < b) ? a : b;
}

int n, m, k;      // n:点数 m:边数 求不超过k条边的最短距离
int dist;      // 从1号点走到每个点的最短距离
int backup;      // dist的备份

// 用自定义结构体存储图(边)
struct Edge
{
      int a, b, w;      // 从 a 到 b 有一条长度为 w 的边
} edges;

// 求出 1 号点到 n 号点,不超过 k 条边的最短距离
int bellman_ford()
{
      // 初始化距离
      memset(dist, 0x3f, sizeof(dist));
      dist = 0;

      // 迭代 k 次(需要求几条边,就循环几次)
      for (int i = 0 ; i < k ; ++i)
      {
                // 将 dist 备份到 backup 中
                // 不进行备份的话,可能会发生“串联”
                // 备份,以保证更新只使用上一次迭代的结果
                memcpy(backup, dist, sizeof(dist));

                // 枚举所有边
                for (int j = 0 ; j < m ; ++j)
                {
                        // 将这一次枚举到的数据先“拷”下来(写得代码短一点)
                        int a = edges.a;
                        int b = edges.b;
                        int w = edges.w;

                        // 更新最短距离
                        dist = min(dist, backup + w);
                }
      }

      // 如果没有路径,返回
      if (dist > 0x3f3f3f3f / 2)
                return -2;
      // 返回最短距离
      return dist;
}

int main()
{
      // 输入
      scanf("%d %d %d", &n, &m, &k);

      // 读入 m 条边
      int a, b, w;
      for (int i = 0 ; i < m ; ++i)
      {
                scanf("%d %d %d", &a, &b, &w);
                edges = {a, b, w};
      }

      // Bellman-Ford算法
      int t = bellman_ford();

      // 输出
      if (t == -2)
                printf("impossible\n");
      else
                printf("%d\n", t);
      return 0;
}
```
页: [1]
查看完整版本: 单源最短路算法:Bellman-Ford 学习笔记