当前位置: 首页 > article >正文

代码随想录算法训练day66---图论系列10《Bellman_ford 队列优化算法负权回路单源有限最短路》

代码随想录算法训练

—day66

文章目录

  • 代码随想录算法训练
  • 前言
  • 一、94. 城市间货物运输 I—Bellman_ford 队列优化算法(又名SPFA)
  • 二、95. 城市间货物运输 II---bellman_ford之判断负权回路
  • 三、96. 城市间货物运输 III---bellman_ford之单源有限最短路
  • 总结


前言

今天是算法营的第66天,希望自己能够坚持下来!
今天继续图论part!今日任务:
● Bellman_ford 队列优化算法(又名SPFA)
●bellman_ford之判断负权回路
●bellman_ford之单源有限最短路


一、94. 城市间货物运输 I—Bellman_ford 队列优化算法(又名SPFA)

卡码网题目链接
文章讲解

Bellman_ford 算法每次松弛 都是对所有边进行松弛。
但真正有效的松弛,是基于已经计算过的节点在做的松弛。
所以其实只需要对 上一次松弛的时候更新过的节点作为出发节点所连接的边 进行松弛就够了。

那么怎么知道上次松弛更新的节点呢?用队列记录。(其实用栈也行,对元素顺序没有要求)

思路:
1.用队列记录上次松弛更新的节点,每次从队列弹出节点去找当前节点连接的所有节点来更新minDist。
2.因为需要找当前节点连接的所有节点,那么用邻接表会方便一些
3.因为有权值,所以定义一个Edge结构体比较方便
4.再加一个bool数组来标记已经在列表中的节点,避免重复加入队列

代码如下:

#include<iostream>
#include<vector>
#include<list>
#include<climits>
#include<queue>
using namespace std;
 
struct Edge { //邻接表
    int to; //链接的节点
    int val; //边的权重
 
    Edge(int t, int v): to(t), val(v) {} //构造函数
};
 
int main() 
{
    int n, m, p1, p2, val;
 
    cin >> n >> m;
    vector<list<Edge>> grid(n+1);
    vector<bool> isInQueue(n+1); //加入优化, 已经在队列里的元素不用重复添加
 
    //将所有边保存起来
    while (m--) {
        cin >> p1 >> p2 >> val;
        grid[p1].push_back(Edge(p2, val));
    }
 
    int start = 1;
    int end = n;
    vector<int> minDist(n+1, INT_MAX);
    minDist[start] = 0;
 
    queue<int> que;
    que.push(start);
 
    while (!que.empty()) {
        int node = que.front(); que.pop();
        isInQueue[node] = false; //从队列里取出来的时候,要取消标记,我们只保证已经在队列里的元素不重复添加
 
        for (Edge edge: grid[node]) {
            int from = node;
            int to = edge.to;
            int value = edge.val;
 
            if (minDist[to] > minDist[from] + value) {
                minDist[to] = minDist[from] + value;
                if (!isInQueue[to]) {
                    que.push(to);
                    isInQueue[to] = true;
                }
            }
        }
    }
    if (minDist[end] == INT_MAX) cout << "unconnected" << endl;
    else  cout << minDist[end] << endl;
}

二、95. 城市间货物运输 II—bellman_ford之判断负权回路

卡码网题目链接
文章讲解

本题因为存在负权回路,如果用上面的方法,会导致一直在负权回路中循环找最小值而进入死循环。

在没有负权回路的图中,松弛 n 次以上 ,结果不会有变化。
而如果有负权回路的话,松弛 n 次结果就会有变化。

所以我们松弛n次,然后在第n次时候加入一个flag判断就可以了。

代码如下:

#include<iostream>
#include<vector>
#include<climits>
using namespace std;

int main() {
    int n, m, p1, p2, val;
    cin >> n >> m;
    vector<vector<int>> grid;

    while (m--) {
        cin >> p1 >> p2 >> val;
        grid.push_back({p1, p2, val});
    }

    int start = 1;
    int end = n;
    vector<int> minDist(n+1, INT_MAX);
    minDist[start] = 0;

    bool flag = false;
    for (int i = 1; i <= n; i++) { // 这里我们松弛n次,最后一次判断负权回路
        for (vector<int>&side : grid) {
            int from = side[0];
            int to = side[1];
            int value = side[2];

            if (i < n) {
                if (minDist[from] != INT_MAX && minDist[to] > minDist[from] + value) {
                    minDist[to] = minDist[from] + value;
                }
            } else { // 多加一次松弛判断负权回路
                if (minDist[from] != INT_MAX && minDist[to] > minDist[from] + value) {
                    flag = true;
                }
            }
        }
    }

    if (flag) cout << "circle" << endl;
    else if (minDist[end] == INT_MAX) cout << "unconnected" << endl;
    else cout << minDist[end] << endl;
}

三、96. 城市间货物运输 III—bellman_ford之单源有限最短路

卡码网题目链接
文章讲解

思路:

对所有边松弛一次,相当于计算 起点到达 与起点一条边相连的节点 的最短距离。
本题是最多经过 k 个城市, 那么是 k + 1条边相连的节点。
在这里插入图片描述
那么bellman_ford 标准写法是松弛 n-1 次,本题就松弛 k + 1次就好。
但是还需要考虑一个问题,负权回路。
如果存在负权回路的话,这样就造成了一个情况,
即:计算minDist数组的时候,基于了本次松弛的 minDist数值,而不是上一次 松弛时候minDist的数值。
所以在每次计算 minDist 时候,要基于 对所有边上一次松弛的 minDist 数值才行,所以我们要记录上一次松弛的minDist。

代码如下:

#include<iostream>
#include<vector>
#include<climits>
using namespace std;

int main() {
    int n, m, p1, p2, val, src, dst, k;

    cin >> n >> m;
    vector<vector<int>> grid;

    while (m--) {
        cin >> p1 >> p2 >> val;
        grid.push_back({p1, p2, val});
    }

    cin >> src >> dst >> k;

    vector<int> minDist(n + 1, INT_MAX);
    vector<int> minDist_copy(n + 1); //用来记录上一次遍历的结果
    minDist[src] = 0;

    for (int i = 1; i <= k + 1; i++) {
        minDist_copy = minDist; //获取上一次计算的结果
        for (vector<int>& side : grid) {
            int from = side[0];
            int to = side[1];
            int value = side[2];
            //注意使用minDist_copy来计算minDist
            if (minDist_copy[from] != INT_MAX && minDist[to] > minDist_copy[from] + value) {
                minDist[to] = minDist_copy[from] + value;
            }
        }
    }

    if (minDist[dst] == INT_MAX) cout << "unreachable" << endl;
    else cout << minDist[dst] <<endl;
}

总结

1.Bellman_ford 队列优化算法,使用队列保存上一次松弛更新的节点,使用bool
数组避免重复加入队列
2.bellman_ford之判断负权回路:对所有边进行n次松弛,在第n次时候判断是否仍然有变化,是则证明有回路。
3.bellman_ford之单源有限最短路:只能经过k个城市,那么就是k+1条边,对所有边进行k+1次松弛,其中需要注意的是,要用另外一个数组minDist_copy来保存上一次minDist,更新minDist时要用minDist_copy来计算。

明天继续加油!


http://www.kler.cn/a/568344.html

相关文章:

  • 26、IO流(只是小入门)
  • IDEAPyCharm安装ProxyAI(CodeGPT)插件连接DeepSeek-R1教程
  • Ollama 的庐山真面目
  • 【树莓派学习】树莓派3B+的安装和环境配置
  • GIT工具学习【1】:基本操作
  • 数据库Redis数据库
  • 【中等】707.设计链表
  • zookeeper-docker版
  • JVM基础概念作用类加载运行时数据区执行引擎本地方法
  • 5G学习笔记之BWP
  • 【Java基础】Java 中 的`final` 关键字
  • 【计算机网络入门】初学计算机网络(六)
  • 车载电源管理新标杆NCV8460ADR2G 在汽车电子负载开关中的应用
  • 基于STM32单片机物联网智能浇花系统设计
  • 请解释 Node.js 中的网络模块(http、https),如何创建 HTTP服务器?
  • 基于微信小程序的疫情互助平台(源码+lw+部署文档+讲解),源码可白嫖!
  • HTMLS基本结构及标签
  • 【星云 Orbit-F4 开发板】06. 串口密码:USART 数据传递
  • 未来该如何选择编程语言?
  • Q-Former 的设计与应用