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

B e l l m a n --- F o r d Bellman---Ford Bellman---Ford是一种单源最短路径算法,可以用于边权为负的图,但是只能用于小图。

大概过程:

  1. 枚举每一条边,更新可以更新的节点(起点到自己距离为 0 0 0,从地点开始向外)。
  2. 重复第一个步骤 n − 1 n - 1 n−1次(起点不用),每一轮至少有一个节点会被更新出最短路径(和 D i j k s t r a Dijkstra Dijkstra中用到的贪心思想有点像)。

Dijkstra传送门

算法复杂度:很明显需要 n − 1 n - 1 n−1个点都需要枚举一次,每次都需要枚举 m m m条边,复杂度为 O ( n m ) O(nm) O(nm)。

同时这个算法还可以判断是否存在负环。只要更新完 n − 1 n - 1 n−1次后,还有点可以被更新最短路,那就是存在负环的,因为只有负环是每走一圈路径长度都会往下减,就可以无限更新,而正常图我们只要枚举 n − 1 n - 1 n−1遍。

也可以记录每个节点最短路的路径。(前面发过的最短路算法应该也有,可以参考 B e l l m a n F o r d Bellman_Ford BellmanFord的处理办法)

同样的,通过例题理解代码。


【模板】Bellman-Ford算法-StarryCoding | 踏出编程第一步

题目描述

n n n点 m m m边的带负权有向图(连通,可能存在重边与自环),求 1 1 1到所有点的单源最短路的距离。

保证结点 1 1 1可以到达所有结点。

如果图中存在负环,则只输出一个整数 − 1 −1 −1。

输入描述

第一行两个整数 n , m 。 ( 2 ≤ n , m ≤ 1 × 1 0 4 ) n, m。(2 \leq n , m \leq 1 \times 10^4) n,m。(2≤n,m≤1×104)

接下来 m m m行,每行一条单向边 x , y , z x,y,z x,y,z表示存在一条从 x x x到 y y y的距离为 z z z的通道。 ( 1 ≤ x , y ≤ n , − 1 0 9 ≤ z ≤ 1 0 9 ) (1 \leq x, y \leq n, -10^9 \leq z \leq 10^9) (1≤x,y≤n,−109≤z≤109)

输出描述

一行 n n n个整数,第 i i i个整数表示从点 1 1 1到点 n n n的最短距离。

如果图中存在负环,则只输出一个整数 − 1 −1 −1。

输入样例1

复制代码
5 5
1 2 1
2 3 -2
3 4 1
4 5 6
1 5 -5

输出样例1

复制代码
0 1 -1 0 -5

解

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 9;
using ll = long long;
const ll inf = 2e18;

struct Edge
{
    int x;
    ll w;
};

int n, m;
vector<Edge> g[N];
ll d[N];
//记录前驱节点,用于打印路径。
// int pre[N];

void print(int s, int t)        //打印路径用的
{
    if(s == t)
    {
        cout << s << ' ';
        return;
    }

    print(s, pre[t])
    cout << t << ' ';
}

void solve()
{
    cin >> n >> m;
    for(int i = 1; i <= m; ++i)
    {
        int u, v;
        ll w; cin >> u >> v >> w;
        g[u].push_back({v, w});
    }

    //d[i]表示从起点到点i的距离。
    for(int i = 1; i <= n; ++i) d[i] = inf;
    d[1] = 0;

    bool circle;    //判断负环,最后一次出来之后还是true就是一直在更新,有负环
    for(int i = 1; i <= n; ++i) //枚举n遍
    {
        circle = false;
        for(int x = 1; x <= n; ++x)     //枚举每天边
        {
            for(auto [y, w] : g[x])
            {
                if(d[x] + w < d[y])     //如果能更新
                {
                    d[y] = d[x] + w;
                    // pre[x] = y;  如有需要,记录路径
                    circle = true;
                }
            }
        }
    }

    if(circle) cout << "-1" << '\n';
    else
    {
        for(int i = 1; i <= n; ++i) cout << d[i] << ' ';
    }
}

int main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int _ = 1;
    while(_--) solve();
    return 0;
}

易错提醒:还是别忘记初始化,别忘记初始化,别忘记初始化。

P S PS PS :这个代码过不了这个例题,数据范围略大,需要优化成 s p f a spfa spfa算法。

相关推荐
Answer1st26 分钟前
【嵌入式模块】UART通信模块
笔记
和侧1 小时前
离子注入后为什么必须配套退火工艺?
笔记·学习
生态学者1 小时前
Global Change Biology | 森林越“步调一致”,反而越怕干旱?树木生长与叶片活动的同步之谜
大数据·人工智能·算法·r语言·微信公众平台
凛铄linshuo1 小时前
自动化测试:pytest第一个实例学习
学习·pytest
lsswear2 小时前
Agent 框架新手入门与实战部署指南
大数据·人工智能·算法
我命由我123453 小时前
智能助手通用问答边界
人工智能·python·学习·ai·学习方法·ai编程·智能体
鬼手点金3 小时前
Agent 的学习机制:后训练、上下文学习与外部化学习
java·人工智能·git·学习·自动化·nanogpt
几个几个n3 小时前
LaTeX 入门学习
学习
Data analyse4563 小时前
rnweb 回放数据量太大怎么办?分片存储与断点续传的实现思路
前端·网络·算法·数据分析
TheBlackHoleLab5 小时前
【Vulnhub系列】DarkHole1: 渗透测试笔记,没有踩渗透的坑,却踩到了虚拟机平台的坑,人生处处是大坑
笔记·vulnhub·easy·darkhole1