【第二十二课】最短路:bellman_ford / spfa算法 (acwing-851 / acwing-853 / c++代码)

目录

前言

acwing-853

bellman_ford算法的思想

代码如下

一些解释

acwing-851

spfa算法思想

代码如下

一些解释 


 

前言

由于权重可以表示不同的度量,例如距离、时间、费用等,具体取决于问题的背景,因此会存在一些权值为负数的题目。也就是存在负权边的最短路问题。

dijkstra算法由于每次都选择当前最短路径的节点进行扩展,并不能解决带有负权值的最短路问题。会存在如下图这样的问题

根据dijkstra的算法思路,我们会先确定A->C的最短路径是1,但其实,A可以先到B再到C ,这样最短距离是-2.

于是有了bellman_ford算法和SPFA算法专门解决有负权值的最短路问题。

acwing-853

对于有负权边的有边数限制的最短路问题,只能用 bellman_ford算法 解决。

bellman_ford算法的思想

通过迭代逼近从源节点到其他所有节点的最短路径。

也就是我们并没有像dijkstra算法一样 直接一次找到当前直连的最短的就不在管这个点即使之后再发现负权边,也没办法再更新了

而是通过有限制的迭代次数,尽可能的找到最短距离。

我们迭代的内容就是:遍历所有的边,通过松弛操作更新该点最短距离。松弛操作也就是下面这段代码的内容,只是个名字而已知道就好

dist[v] = min(dist[v], dist[u] + weight(u, v));

这里需要注意的就是:可能我们A点进行了dist数组的更新,我们的更新就是通过加了一条边实现的,那么在同一轮迭代中的后续更新,可能会使用前面的更新之后的结果,这样就可能导致一条路径上的边被计算了多次,从而违反了最多只能经过k条边的限制

因此我们要创建一个backup数组,每次迭代之前就将初始的dist数组复制到backup里,这样每次在这个备份上进行更新,就可以确保每个节点在当前轮次中只受到一次更新的影响,而不受到之前已经更新的影响。

该算法适用于带有负权边的图,并且能够检测负权环的存在。

如何检测负权环的呢?

一般需要在算法执行完毕后,再进行额外的检查。通常,检查方法是再进行一轮迭代,如果某个节点的最短路径在这一轮仍然被更新,那么就说明存在负权环

代码如下

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=510,M=10010;
int n,m,k;

int dist[N],backup[N];
struct Edge{
    int a,b,w;
}edges[M];
int bellmain_ford()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    // k 次迭代可以确保在有限次迭代内 找到从源节点到其他节点的最短路径
    for(int i=0;i<k;i++)
    {
        memcpy(backup,dist,sizeof dist);//每次迭代之前,存好上次dist更新的结果
        for(int j=0;j<m;j++)//对每条边进行松弛操作
        {
            int a=edges[j].a;
            int b=edges[j].b;
            int w=edges[j].w;
            dist[b]=min(dist[b],backup[a]+w);//松弛操作
        }
    }

    if(dist[n]>0x3f3f3f3f/2)return -1;
    return dist[n];
}
int main()
{
    cin>>n>>m>>k;

    for(int i=0;i<m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        edges[i]={a,b,c};
    }
    int t=bellmain_ford();
    if(t==-1)puts("impossible");
    else cout<<t;
    return 0;
}

一些解释

1.在这道题的代码中,我们并没有进行负权环的检测

2.最后是否存在最短距离的判断中

if(dist[n]>0x3f3f3f3f/2)return -1;

我们用0x3f3f3f3f/2作为判断标准是因为

可能有负权值导致最后的结果不等于0x3f3f3f3f,但是也是无限接近于无穷大的,因此dist[n] > 0x3f3f3f3f / 2就可以认为是无穷大了。(其实这种说法我有些疑惑,是在这里不/2也是可以的意思么?en,,不太确定)

这个可以了解一下:

.

我们经常需要一个表示“无穷大”的值,特别是在图论和动态规划等领域。0x3f3f3f3f是一个常用的表示无穷大的值,因为它是一个非常大的正整数。

.

如果你在计算过程中需要将两个“无穷大”的值相加,那么结果可能会超过整数的最大值,导致溢出。这就是为什么在一些情况下,我们会使用0x3f3f3f3f / 2作为无穷大的表示,以避免溢出的问题。 

acwing-851

spfa算法可以解决大部分的带负权值甚至全为正值的图的最短路问题。

spfa算法思想

spfa是对bellman_ford算法的优化。主要优化的地方就在遍历每条边进行松弛操作

for(int j=0;j<m;j++)//对每条边进行松弛操作
    {
        int a=edges[j].a;
        int b=edges[j].b;
        int w=edges[j].w;
        dist[b]=min(dist[b],backup[a]+w);//松弛操作
    }

bellman_ford算法每次迭代都对所有边无差别的进行松弛操作,然鹅我们很容易发现只有当上一次a为顶点 backup[a] 有了更短距离的更改时,以a为顶点指出的顶点才会有距离的更改。

spfa针对这一点进行了优化:利用队列,将每次更改距离的点入队,每次出队一个点,遍历这个点所直连的点,进行距离的判断更改

代码如下

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e5+10;
int n,m;
typedef pair<int,int> PII;
int h[N],e[N],ne[N],w[N],idx;
int dist[N];
bool st[N];
void add(int a,int b,int c)
{
    e[idx]=b;
    ne[idx]=h[a];
    w[idx]=c;
    h[a]=idx++;
}
int spfa()
{
    memset(dist,0x3f,sizeof dist);
    dist[1]=0;
    queue<int> q;
    q.push(1);
    st[1]=1;
    while(q.size())
    {
        int t=q.front();
        q.pop();
        st[t]=0;
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[t]+w[i])
            {
                dist[j]=dist[t]+w[i];
                if(!st[j])
                {
                    q.push(j);
                    st[j]=1;
                }
            }
        }
    }
    if(dist[n]==0x3f3f3f3f)return -1;
    return dist[n];
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    int t=spfa();
    if(t==-1)puts("impossible");
    else cout<<t;
    return 0;
}

这段代码在理解算法思想之后,比较好理解。 

一些解释 

1.st数组

在代码中,我们有三处关于st数组的代码

①就是初始状态,先把源点入队,并将其st值置为1,表示在队列中。

②我们每次对一个点执行操作的时候,将该点出队了,因此先置为0

③每个更新了距离的节点会被入队,因此置为1

我最开始比较疑惑的是②那里。应该是因为之前写的st数组都没有在那样的位置置0的

但是我们要明白这里,st数组的作用是什么。

st数组用于跟踪每个节点是否在队列中。当我们将一个节点加入队列时,我们就将st对应的值设为1。当我们从队列中取出一个节点时,我们就将st对应的值设为0。

在算法的执行过程中,由于节点可能会多次进入和离开队列我们需要在每次从队列中取出节点时更新st数组

下面图中举个例子看一下算法执行的过程,就能更清楚这里所说的,节点可能会多次入队出队,②处代码的作用也更清晰了~

2.时间复杂度

 


先写到这里,,早日恢复状态!!

有问题欢迎指出,一起加油!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/367580.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springboot并mybatis入门启动

pom.xml,需要留意jdk的版本&#xff08;11&#xff09;和springboot版本要匹配&#xff08;2.7.4&#xff09;&#xff0c;然后还要注意mybatis启动l类的版本&#xff08;2.2.2&#xff09; <?xml version"1.0" encoding"UTF-8"?> <project xm…

Visual Studio 2022 查看类关系图

这里写自定义目录标题 右键要查看的项目 -“查看”-“查看类图”效果展示&#xff1a; 原文地址 www.cnblogs.com 步骤1&#xff1a;勾选扩展开发 步骤2: 勾选类设计器 右键要查看的项目 -“查看”-“查看类图” 效果展示&#xff1a;

【Uni-App】运行微信小程序时报错routeDone with a webviewId 2 that is not the current page

使用HBuilderX开发微信小程序&#xff0c;运行项目的时有可能会出现routeDone with a webviewId 1 that is not the current page的报错&#xff0c;但不影响运行。如果强迫症介意的话&#xff0c;可以考下面的方法进行修复。 产生原因 由于微信开发者工具的调试基础库处于灰度…

[python]基于Ultra-Fast-Lane-Detection-v2车道线实时检测onnx部署

【论文地址】 https://arxiv.org/pdf/2206.07389.pdf 【框架地址】 https://github.com/cfzd/Ultra-Fast-Lane-Detection-v2 【框架介绍】 Ultra-Fast-Lane-Detection-v2&#xff08;UFL-D-v2&#xff09;算法是一种高效的车道线检测算法&#xff0c;它旨在快速准确地识别…

常见关系型数据库产品介绍

更新晚了&#xff0c;不好意思啦&#xff01;继关系型数据库的介绍与历史今天主要和大家分享关系型数据库有哪些产品以及简单的背景介绍。这篇文章介意宝宝们听着舒缓的音乐静静享受。 关系型数据库的产品有很多&#xff0c;下面和大家分享一些比较有名的、使用比较广泛的关系…

HP惠普暗影精灵8P笔记本OMEN Gaming Laptop 16-n0076AX原厂Win11系统镜像恢复出厂预装OEM系统

原装Windows11系统安装包&#xff0c;适用型号(HP暗影8plus笔记本电脑)&#xff1a; 16-n0000AX、16-n0001AX、16-n0002AX、16-n0003AX、16-n0004AX、16-n0005AX 16-n0016AX、16-n0058AX、16-n0059AX、16-n0076AX、16-n0078AX等 链接&#xff1a;https://pan.baidu.com/s/1G…

Javaweb之SpringBootWeb案例之yml配置文件的详细解析

4.2 yml配置文件 前面我们一直使用springboot项目创建完毕后自带的application.properties进行属性的配置&#xff0c;那其实呢&#xff0c;在springboot项目当中是支持多种配置方式的&#xff0c;除了支持properties配置文件以外&#xff0c;还支持另外一种类型的配置文件&am…

HTMLCSS JavaScript 基础

HTML复杂建立骨架。 CSS复杂装修。 JS负责定义行为和交互。 示例功能&#xff0c;点击按钮&#xff0c;数量增加&#xff0c;图片交互显示。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"…

HiSilicon352 android9.0 开机视频调试分析

一&#xff0c;开机视频概念 开机广告是在系统开机后实现播放视频功能。 海思Android解决方案在原生Android基础上&#xff0c;增加了开机视频模块&#xff0c;可在开机过程中播放视频文件&#xff0c;使用户更好的体验系统开机过程。 二&#xff0c;模块结构 1. 海思自研开机…

操作系统基础:内存管理概述【下】

&#x1f308;个人主页&#xff1a;godspeed_lucip &#x1f525; 系列专栏&#xff1a;OS从基础到进阶 &#x1f304;1 两级页表&#x1f3d9;️1.1 知识总览&#x1f3d9;️1.2 单极页表存在的问题&#x1f682;1.2.1 假设&#x1f682;1.2.2 结论 &#x1f3d9;️1.3 对第一…

Android之命令行烧写OTA镜像(一百八十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

接上回如何在App Store和Google Play 上获得曝光度

ASO不仅仅是关键词方面的优化&#xff0c;还有很多其他方面的点要注意。 App被评级的次数与其在搜索结果中的排名直接相关&#xff0c;所以要求用户对应用程序进行评分来积极提高评分非常重要&#xff0c;并且这个数字每天都在持续增长。ASO做评论可以帮助覆盖掉负面的评论&am…

带你了解JAVA中的AQS介绍(AbstractQueuedSynchronizer)

一、AQS 介绍 AQS的全称为&#xff08;AbstractQueuedSynchronizer&#xff09;&#xff0c;这个类在java.util.concurrent.locks包下面。 AQS是一个用来构建锁和同步器的框架&#xff0c;使用AQS能简单且高效地构造出应用广泛的大量的同步器&#xff0c;比如我们提到的Reen…

MyBatis笔记梳理

文章目录 什么是 MyBatis&#xff1f;前期准备依赖配置文件mapper利用注解 增、删、改、查查增改删#{} 和 ${} 的区别类型别名 动态sqlwhere ifforeachsql引用不常用标签 多表查询多对一&#xff08;一对一&#xff09;一对多多对多多表查询 个人理解 延迟加载概念使用场景延迟…

Emmet常用语法总结

Emmet常用语法总结 子元素&#xff1a;>兄弟元素&#xff1a;上级元素&#xff1a;^倍数&#xff1a;*分组&#xff1a;&#xff08;&#xff09;属性&#xff1a;[]id和类&#xff1a;# .迭代数字&#xff1a;$文本内容&#xff1a;{}注意事项 Emmet是许多流行文本编辑器的…

Linux下find命令详解

find #查找文件 #按照文件名、大小、时间、权限、类型、所属者、所属组来搜索文件 格式&#xff1a; find 查找路径 查找条件 具体条件&#xff08;按文件名或时间大小等&#xff09; 操作 注意&#xff1a; find命令默认的操作是print输出 find是检索…

【Springcloud篇】学习笔记二(四至六章):Eureka、Zookeeper、Consul

第四章_Eureka服务注册与发现 1.Eureka基础知识 1.1Eureka工作流程-服务注册 1.2Eureka两大组件 2.单机Eureka构建步骤 IDEA生成EurekaServer端服务注册中心&#xff0c;类似于物业公司 EurekaClient端cloud-provider-payment8081将注册进EurekaServer成为服务提供者provide…

【学员分享-考试心得】国产数据库潜力无限,云贝教育OBCP认证培训帮您解难题

近年来&#xff0c;随着国产化转型的推进&#xff0c;国外数据库的岗位需求逐渐减少&#xff0c;让许多IT从业者倍感压力。在这种情况下&#xff0c;了解国产数据库成为了求职市场上的竞争力。云贝老师们将聚焦于OceanBase、PostgreSQL、TDSQL等IT培训&#xff0c;探讨其对国产…

如何计算模型的复杂度(参数量,FLOPs)

参考 如何计算神经网络模型的复杂度 深度学习卷积、全连接层、深度可分离层参数量和FLOPs计算公式 概念 Params&#xff1a;模型的参数量。&#xff08;空间复杂度&#xff09;FLOPs&#xff1a;FLoating point Operations&#xff0c;前向推理的计算量。&#xff08;时间复…

Loadbalancer如何优雅分担服务负荷

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Loadbalancer如何优雅分担服务负荷 前言Loadbalancer基础&#xff1a;数字世界的分配大师1. 分发请求&#xff1a;2. 健康检查&#xff1a;3. 会话保持&#xff1a;4. 可伸缩性&#xff1a;5. 负载均衡…