首页 > 编程语言 >代码随想录算法训练营第六十天|Bellman_ford队列优化法(SPFA)、bellman_ford之判断负权回路、bellman_ford之单元有限最短路

代码随想录算法训练营第六十天|Bellman_ford队列优化法(SPFA)、bellman_ford之判断负权回路、bellman_ford之单元有限最短路

时间:2024-12-28 10:57:26浏览次数:9  
标签:p2 val int 随想录 bellman ford minDist que include

前言

打卡代码随想录算法训练营第49期第六十天(づ ◕‿◕ )づ

首先十分推荐学算法的同学可以先了解一下代码随想录,可以在B站卡哥B站账号、代码随想录官方网站代码随想录了解,卡哥清晰易懂的算法教学让我直接果断关注,也十分有缘和第49期的训练营大家庭一起进步。


Bellman_ford队列优化法(SPFA)

文章讲解:Bellman_ford 队列优化算法(又名SPFA)

使用队列堆Bellman_ford进行优化,使得不用每次都要遍历一下所有边进行松驰。

图越稠密,效率越接近Bellman_ford。图越稀疏,效率越高。

同时也不需要害怕有环的情况,因为我们先判断数值的大小,再加入的队列,所有最后所有边都松驰了,数不变了,也就没有节点加入队列。

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

struct Edge { //邻接表
    int to;  // 链接的节点
    int val; // 边的权重

    Edge(int t, int w): to(t), val(w) {}  // 构造函数
};


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

    vector<list<Edge>> grid(n + 1); 

    vector<bool> isInQueue(n + 1); // 加入优化,已经在队里里的元素不用重复添加

    // 将所有边保存起来
    for(int i = 0; i < m; i++){
        cin >> p1 >> p2 >> val;
        // 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] == false) { // 已经在队列里的元素不用重复添加
                    que.push(to);
                    isInQueue[to] = true;
                }
            }
        }

    }
    if (minDist[end] == INT_MAX) cout << "unconnected" << endl; // 不能到达终点
    else cout << minDist[end] << endl; // 到达终点最短路径
}

Bellman_ford之判断负权回路

文章讲解:bellman_ford之判断负权回路

本题最主要的就是判断负权回路,代码直接使用之前的Bellman_ford代码即可,当松驰n - 1次之后再多松驰一次看是否会有变化,有变化则产生了负权回路。

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

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

    vector<vector<int>> grid;

    for(int i = 0; i < m; i++){
        cin >> p1 >> p2 >> val;
        // 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 price = side[2];
            if (i < n) {
                if (minDist[from] != INT_MAX && minDist[to] > minDist[from] + price) minDist[to] = minDist[from] + price;
            } else { // 多加一次松弛判断负权回路
                if (minDist[from] != INT_MAX && minDist[to] > minDist[from] + price) flag = true;

            }
        }

    }

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

Bellman_ford之单元有限最短路

文章讲解:bellman_ford之单源有限最短路

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

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

    vector<vector<int>> grid;

    for(int i = 0; i < m; i++){
        cin >> p1 >> p2 >> val;
        grid.push_back({p1, p2, val});
    }

    cin >> src >> dst >> k;

    vector<int> minDist(n + 1 , INT_MAX);
    minDist[src] = 0;
    vector<int> minDist_copy(n + 1); // 用来记录上一次遍历的结果
    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 price = side[2];
            // 注意使用 minDist_copy 来计算 minDist 
            if (minDist_copy[from] != INT_MAX && minDist[to] > minDist_copy[from] + price) {  
                minDist[to] = minDist_copy[from] + price;
            }
        }
    }
    if (minDist[dst] == INT_MAX) cout << "unreachable" << endl; // 不能到达终点
    else cout << minDist[dst] << endl; // 到达终点最短路径

}

SPFA写法 

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

struct Edge { //邻接表
    int to;  // 链接的节点
    int val; // 边的权重

    Edge(int t, int w): to(t), val(w) {}  // 构造函数
};


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

    vector<list<Edge>> grid(n + 1); // 邻接表

    // 将所有边保存起来
    for(int i = 0; i < m; i++){
        cin >> p1 >> p2 >> val;
        // p1 指向 p2,权值为 val
        grid[p1].push_back(Edge(p2, val));
    }
    int start, end, k;
    cin >> start >> end >> k;

    k++;

    vector<int> minDist(n + 1 , INT_MAX);
    vector<int> minDist_copy(n + 1); // 用来记录每一次遍历的结果

    minDist[start] = 0;

    queue<int> que;
    que.push(start); // 队列里放入起点

    int que_size;
    while (k-- && !que.empty()) {

        vector<bool> visited(n + 1, false); // 每一轮松弛中,控制节点不用重复入队列
        minDist_copy = minDist; 
        que_size = que.size(); 
        while (que_size--) { 
            int node = que.front(); que.pop();
            for (Edge edge : grid[node]) {
                int from = node;
                int to = edge.to;
                int price = edge.val;
                if (minDist[to] > minDist_copy[from] + price) {
                    minDist[to] = minDist_copy[from] + price;
                    if(visited[to]) continue; // 不用重复放入队列,但需要重复松弛,所以放在这里位置
                    visited[to] = true;
                    que.push(to);
                }
            }

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

Bellman_ford变种真的多,需要多练习和学习。

标签:p2,val,int,随想录,bellman,ford,minDist,que,include
From: https://blog.csdn.net/tancxiaohei23/article/details/144782592

相关文章

  • 代码随想录——动态规划9不同的二叉搜索树
    解题思路本题通过递归和二叉搜索树特性解决。当n=1时,结果是1。如果n>1时,因为根节点值不同对应的二叉搜索树肯定不同,所以我们考虑根为i(2≤i≤n)的情况。由二叉搜索树特性,根左边一定有i-1个元素,右边一定有n-i个元素。设f(i)函数返回i个不同元素节点组成的二叉搜索树的个数。......
  • 代码随想录算法训练营第五十九天|dijkstra(堆优化版)精讲、Bellman_ford
    前言打卡代码随想录算法训练营第49期第五十九天⚆_⚆(˘❥˘)(•̀⌓•)シ(人•͈ᴗ•͈)♡♡首先十分推荐学算法的同学可以先了解一下代码随想录,可以在B站卡哥B站账号、代码随想录官方网站代码随想录了解,卡哥清晰易懂的算法教学让我直接果断关注,也十分有缘和第49期的......
  • 代码随想录——贪心23监控二叉树
    思路这道题目首先要想,如何放置,才能让摄像头最小的呢?从题目中示例,其实可以得到启发,我们发现题目示例中的摄像头都没有放在叶子节点上!这是很重要的一个线索,摄像头可以覆盖上中下三层,如果把摄像头放在叶子节点上,就浪费的一层的覆盖。所以把摄像头放在叶子节点的父节点位置,才......
  • 代码随想录算法训练营第二十九天|134加油站、135分发糖果、860柠檬水找零、406根据身
    day29贪心算法part031.134加油站解题思路:(1)总体条件判断:如果gas数组的总和小于cost数组的总和,则无论从哪个加油站出发,汽油都不足以完成一圈,返回-1。(2)寻找起点:设定两个变量:total_tank:总剩余汽油量,表示从头到尾累计整个数组的油量差,目的是判断总的油量......
  • 代码随想录算法训练营第五十五天|并查集理论基础、寻找存在的路径
    前言打卡代码随想录算法训练营第49期第五十五天(~ ̄▽ ̄)~首先十分推荐学算法的同学可以先了解一下代码随想录,可以在B站卡哥B站账号、代码随想录官方网站代码随想录了解,卡哥清晰易懂的算法教学让我直接果断关注,也十分有缘和第49期的训练营大家庭一起进步。学习今天的课程前,先看并......
  • 代码随想录算法训练营第五天-哈希-242.有效的字母异位词
    这道题的总体感觉不是很难,但是其完成的思想还是很有趣的利用数据下标来代表字母序列然后遍历两个字符串每个字符,给对应字母下标的数组中一个自增,另一个自减通过查看最后的数组内容是不是0,来判断是不是异位词#include<iostream>#include<array>classSolution{public......
  • GPT 论文作者 Alec Radford 离开 OpenAI,曾参与开发 Whisper;闪极 AI 拍照眼镜支持全天
       开发者朋友们大家好: 这里是「RTE开发者日报」,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享RTE(Real-TimeEngagement)领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「有看点的会议」,但内容仅代表......
  • 代码随想录算法训练营第二十七天|455分发饼干、376摆动序列、53最大子序和
    day27贪心算法part01贪心算法没有什么规律可言,没有思路就立刻看题解。1.455分发饼干为了满足更多的小孩,就不要造成饼干尺寸的浪费。大尺寸的饼干既可以满足胃口大的孩子也可以满足胃口小的孩子,那么就应该优先满足胃口大的。先将饼干数组和小孩数组排序,然后从后向......
  • 【代码随想录】刷题记录(76)-子集
    题目描述:给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例1:输入:nums=[1,2,3]输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]示例2:输入:nums=[0]输出:[[......
  • 代码随想录算法训练营第三十二天|动态规划理论基础|LC509.肥波那些数|LC70.爬楼梯|LC7
    动态规划理论基础    解释:动态规划,英文:DynamicProgramming,简称DP;如果某一问题有很多重叠子问题,使用动态规划是最有效的。动态规划五部曲:    1、确定dp数组(dptable)以及下标的含义;    2、确定递推公式;    3、dp数组如何初始化;   ......