算法学习笔记(26)——Bellman-Ford算法(单源最短路)

Bellman-Ford 算法

Bellman-Ford算法用于在包含负权边的图上,求单源点最短路的问题,时间复杂度 O(nm) 。但是因为该算法的改进版SPFA,在求单源点最短路的问题上几乎总是优于Bellman-Ford算法,所以Bellman-Ford算法一般只应用在对边的数量有限制的最短路问题,即限制经过边的数量不超过 k

Bellman-Ford算法属于动态规划算法。

Bellman-Ford算法的基本思想是,如果要求最短路的长度最多为 k(如果不限制,那其实就是 k=n1,因为 n 个点没有环的情况下最多有 n1 条边,所以长度大于等于 n 的路径一定有环,也就一定是负环了,但是存在负环的路径不能作为最短路,不然就可以一直转让路径越来越小),那么执行下面的过程:

dist[N]记录距离,last[N]作为dist[N]的副本,两者清空为正无穷
到源点1自己的距离是0,所以dist[1] = 0
for k次
    将上轮算好的dist[N]记录到副本last[N]里,防止更新dist[N]时候用的不是上轮的值
    for 所有边(a, b, w) 表示从a到b有权值为w的边
        dist[b] = min(dist[b], dist[a] + w) // 松弛操作:其思想也是看看先到a再到b会不会更短

n 次循环后一定满足dist[b] <= dist[a] + w,迭代 k 次,则求出不超过 k 条边的最短距离。

最后,数组dist[N]里记录的就是从源点 1 ,不超过 k 步,到其它所有点的最短距离。

  • 如果求的是在最长 k 步这个限制下的最短路,那么时间复杂度是 O(km)
  • 如果求的就是对路径长度 k 没有要求的最短路,那么 k 就是 n1,这个时候算法的时间复杂度是 O(mn)

由于Bellman-Ford算法只要求能把所有边遍历就行了,所以在实现的时候既不需要用邻接矩阵存,也不需要写邻接表,直接开数组,把所有边存数组里就行了。

struct Edge {
	int a, b, w;
}edges[M];

题目链接:AcWing 853. 有边数限制的最短路

#include <iostream>
#include <cstring>

using namespace std;

const int N = 510, M = 1e4 + 10;

// 边数组,从a到b有权值为w的边
struct Edge {
    int a, b, w;
}edges[M];

// n个结点,m条边,最多k步最短路
int n, m, k;

// dist存到每个点不超过i步的最短路,经过k次松弛就是不超过k步的最短路
// last是dist的副本,防止一次外层循环里出现结点之间互相松弛的情况
int dist[N], last[N];

// 求最长不超过k步的最短路,存到dist数组里
void bellman_ford() {
    // 清空dist为inf,因为刚循环就会复制到last里,所以last不用清
    memset(dist, 0x3f, sizeof dist);
    // 源点到源点(编号1)的距离为0
    dist[1] = 0;
    // 最长不超过k步,所以松弛操作只执行k词
    for (int i = 0; i < k; i ++ ) {
        // 先将dist暂存到last里
        memcpy(last, dist, sizeof dist);
        // 对edges里存的每条边做松弛操作
        for (int j = 0; j < m; j ++ ) {
            auto& e = edges[j];
            dist[e.b] = min(dist[e.b], last[e.a] + e.w);
        }
    }
}

int main() {
    cin >> n >> m >> k;
    // 所有的边直接读入到数组里
    for (int i = 0; i < m; i ++ )
        cin >> edges[i].a >> edges[i].b >> edges[i].w;
    // 计算经过最多k步的最短路,存到dist里
    bellman_ford();
    // 注意由于存在负权边,inf可以拿来更新,所以判断不存在是用> inf/2
    if (dist[n] > 0x3f3f3f3f / 2) cout << "impossible" << endl;
    else cout << dist[n] << endl;
    return 0;
}
posted @   S!no  阅读(147)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示