Bellman-Ford算法(详解版)

Bellman-Ford算法

Bellman-Ford算法是用来解决,对于有负权的图的**单源最短路径**.因为DJ算法不可以解决对于负权的图,所以使用这个算法来求解.但是依旧不可以有负回路.因为负回路就没有存在单源最短路径这一说.

BF的另一个重要的用途就是用来检测**是不是存在负回路**

思路:

  • 就是考察每一条边,假设为边的源头是a,边的终点是b,边的权重是len.那么考察这条边所能到达的点b,如果存在dis[a]!=INT_MAX && dis[a]+len<dis[b],那么就说明可以松弛.其中dis[index]表示源点到index的最短距离.

  • 所以采用**边集数组**来存图.边的遍历顺序可以随便指定.

  • 思考最坏的遍历的情况,每一次遍历,只能有一个点可以通过松弛操作,把dis[index]更新,那么n个点就需要n-1次松弛操作.因为(源点到源点的距离为0不需要更新)

  • 所以从上述的描述可以看出,算法的时间复杂度为 O ( V × M ) \text O(V \times M) O(V×M)其中V为点的数量,M为边的数量.

  • 如果进行完成V*M之后还可以再进行一次松弛操作就是还存在dis[a]!=INT_MAX && dis[a]+len<dis[b].那么就是存在负环.

  • 如果是用来检测从某个点是不是可以到达负回路(负环).就初始化dis[src]=0,其余的值都为无穷大,

  • 但是如果要判断整个图是不是存在负回路的话就要使用到虚拟源点的思路.

  • 具体来说就是初始化dis[ALL]=0设置一个虚拟源点到所有的点的距离为0.就是和所有的点都有连接.

注意:

判断整个图是不是存在负环和判断src出发是不是存在负环,是不一样.因为从src可能压根就达到不了那个负环,就是图不是连通的那么就非常有可能**判断不出来那个负环.**

就是关于Bellman-ford算法必须知道的几个最:

  • 最多进行点数-1轮所有边的松弛,就可以更新所有的最短路径.因为最坏的情况下就是遍历完成所有的边之后只能更新一个点的最短路径.
  • 每个点最多被松弛点数-1.如果进行了点数次松弛说明就有负环.因为考察每条边的次数是点数-1轮.每一轮都会遍历所有的边.所以存在每一次都会松弛这个点一次的情况.

代码:

dis数组存储单源最短路径,并且判断是不是可以到达负环.(不能判断整个图是不是存在负环)

#include <bits/stdc++.h>
using namespace std;
struct Edge
{int u,v,len;
};
int dis[10003];
int main()
{int n,m;cin>>n>>m;int start=0;cin>>start;Edge edges[m+2];for(int i=0;i<m;i++){cin>>edges[i].u>>edges[i].v>>edges[i].len;}int dis[n+2];for(int i=0;i<n;i++){dis[i]=INT_MAX;}dis[start]=0;bool flag=false,huan=false;for(int i=0;i<n-1;i++){flag=false;for(int j=0;j<m;j++){if(dis[edges[j].u]!=INT_MAX&&dis[edges[j].v]>dis[edges[j].u]+edges[j].len){flag=true;dis[edges[j].v]=dis[edges[j].u]+edges[j].len;}}if(flag==false)break;//如果没有存在松弛操作就可以提前退出了}if(flag==false){//在进行一次遍历,看一看是不是还存在松弛的点for(int j=0;j<m;j++){if(dis[edges[j].u]!=INT_MAX&&dis[edges[j].v]>dis[edges[j].u]+edges[j].len){huan=true;break;}}if(huan)cout<<"有负环"<<endl;}return 0;
}

如果判断图中是不是存在负环,除了要设置虚拟源点外还要**进行V次松弛操作**.因为点都要出来;代码如下:

#include <bits/stdc++.h>
using namespace std;
struct Edge
{int u,v,len;
};
int dis[10003];
int main()
{int n,m;cin>>n>>m;int start=0;cin>>start;Edge edges[m+2];for(int i=0;i<m;i++){cin>>edges[i].u>>edges[i].v>>edges[i].len;}int dis[n+2];for(int i=0;i<n;i++){dis[i]=0;}dis[start]=0;bool flag=false,huan=false;for(int i=0;i<n;i++){flag=false;for(int j=0;j<m;j++){if(dis[edges[j].u]!=INT_MAX&&dis[edges[j].v]>dis[edges[j].u]+edges[j].len){flag=true;dis[edges[j].v]=dis[edges[j].u]+edges[j].len;}}if(flag==false)break;//如果没有存在松弛操作就可以提前退出了}if(flag==false){//在进行一次遍历,看一看是不是还存在松弛的点for(int j=0;j<m;j++){if(dis[edges[j].u]!=INT_MAX&&dis[edges[j].v]>dis[edges[j].u]+edges[j].len){huan=true;break;}}if(huan)cout<<"有负环"<<endl;}return 0;
}

如果对建图的知识还不了解的话,可以参考我写的这篇高质量博客(但是不知道为什么观看量不太高,明明很好.)文章链接:

建图大法好
一定要好好的揣摩我写的这篇建图博客,一定会对你有所帮助的.

上述中常数时间可以被优化一下,就是利用队列来优化一下,有时候并不会要求对所有的边都需要进行考察,而是对于那些有了松弛的点所连接的边才需要进行下一轮的松弛考察.因为这个点被松弛了,所以从他开始的边所达到的点,才有可能被松弛.所以只考察,被松弛的点所连接的边即可.因为设计到点所连接的边.所以使用领接表建图.不在使用边集数组.

测试链接

SPFA优化如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
//首先建图,使用邻接表建图,u=pair.first,v=pair.second.
//遍历所有的边.dis[v]=min(dis[v],dis[u]+arr[u][i].second
inline ll read()
{ll f = 0, s = 0;char ch = getchar();while (!isdigit(ch)) f |= ch == '-', ch = getchar();while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();return f ? -s : s;
}
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
int main()
{int ci;cin>>ci;while(ci--){int n,m;cin>>n>>m;vector<PII>arr[n+2];int dis[n+2];int dui[4000002];int l=0,r=0;bool flag[n+1];//建图for(int i=0;i<m;i++){int a,b,c;cin>>a>>b>>c;if(c>=0){arr[a].push_back({b,c});arr[b].push_back({a,c});}elsearr[a].push_back({b,c});}//初始化数组for(int i=1;i<=n;i++){dis[i]=INT_MAX;flag[i]=false;}//将源点放进队列dis[1]=0;dui[r++]=1;flag[1]=true;bool ans=false;int cnt[n+2]={0};//统计每个点被松弛的次数,更新距离才算松弛cnt[1]++;//因为1距离更新了while(l!=r){int index=dui[l++];flag[index]=false;for(int i=0;i<arr[index].size();i++){if(dis[index]!=INT_MAX&&dis[index]+arr[index][i].second<dis[arr[index][i].first]){dis[arr[index][i].first]=dis[index]+arr[index][i].second;//距离更新了所以要,将松弛次数++if(cnt[arr[index][i].first]++ == n){ans=true;break;}if(flag[arr[index][i].first]==false){dui[r++]=arr[index][i].first;flag[arr[index][i].first]=true;}}}if(ans)break;}if(ans)cout<<"YES"<<endl;elsecout<<"NO"<<endl;}return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《HarmonyOSNext的ForEach数组渲染の核心玩法与避坑指南》

《HarmonyOSNext教学宝典&#xff1a;ForEach数组渲染全攻略与性能优化》 #HarmonyOS开发 #ArkTS实战 #组件解析 &#x1f3af; ForEach组件完全指南&#xff1a;数组循环渲染核心机制 举个栗子&#x1f330;&#xff1a; ForEach相当于智能印刷机&#xff0c;将数组元素自动转…

单片机 - STM32F407 ADC 模式详解:单次转换、连续转换、扫描模式、非扫描模式

STM32F407 ADC 模式详解&#xff1a;单次转换、连续转换、扫描模式、非扫描模式 前言 在 STM32F407 中&#xff0c;ADC&#xff08;模数转换器&#xff09;模块常用于采集模拟信号&#xff0c;比如读取光敏电阻、电压、电流、温度传感器等。STM32 的 ADC 模式较多&#xff0c…

开源模型应用落地-工具使用篇-从零开始搭建Qdrant Web UI-可视化管理工具-Windows(十)

一、前言 Qdrant 是一个高性能的向量搜索引擎&#xff0c;广泛应用于相似性搜索、推荐系统和大规模数据检索等场景。虽然其原生 API 提供了强大的功能&#xff0c;但对于开发者和运维人员来说&#xff0c;缺乏直观的可视化界面常常增加了使用门槛。为了解决这一问题&#xff0c…

高频交易技术:订单簿分析与低延迟架构——从Level 2数据挖掘到FPGA硬件加速的全链路解决方案

高频交易技术&#xff1a;订单簿分析与低延迟架构——从Level 2数据挖掘到FPGA硬件加速的全链路解决方案 一、引言&#xff1a;高频交易的技术本质 1.1 速度即利润的微观战场 数据揭示&#xff1a;据NYSE实测&#xff0c;每降低1微秒延迟可获得年化$700-1500万套利窗口&#…

基于生成对抗网络(GAN)的图像生成与编辑:原理、应用与实践

前言 生成对抗网络&#xff08;GAN&#xff09;是近年来深度学习领域中最具影响力的技术之一。自2014年由Ian Goodfellow等人首次提出以来&#xff0c;GAN已经在图像生成、图像编辑、风格转换等多个领域取得了令人瞩目的成果。GAN的核心思想是通过生成器&#xff08;Generator&…

pytorch基本运算-梯度运算:requires_grad_(True)和backward()

引言 前序学习进程中&#xff0c;已经对pytorch基本运算中的求导进行了基础讨论&#xff0c;相关文章链接为&#xff1a; 导数运算pytorch基本运算-导数和f-string-CSDN博客 实际上&#xff0c;求导是微分的进一步计算&#xff0c;要想求导的前一步其实是计算微分&#xff1…

idea64.exe.vmoptions配置

这个idea64.exe.vmoptions文件是用于配置 IntelliJ IDEA&#xff08;64位版本&#xff09;运行时的 Java 虚拟机&#xff08;JVM&#xff09;参数。这些参数直接影响到 IDEA 的性能、内存使用、调试能力和行为。 下面是对文件中每一行配置的详细解读&#xff1a; -Xms2048m 作…

齐次变换矩阵相乘的复合变换:左乘与右乘的深度解析

在三维几何变换中,齐次变换矩阵相乘是实现复杂变换的核心方法。本文将通过一个包含四个变换步骤的完整示例,深入探讨齐次变换矩阵左乘和右乘的区别,并结合 Python sympy 库的代码实现,详细阐述变换过程和结果差异。 二维齐次坐标的旋转变换 在二维齐次坐标系中,一个点可以…

5g LDPC编译码-LDPC编码

目录 1、LDPC编码基础知识 2、5g的LDPC编码 2.1 LDPC分块: 2.2 LDCP编码 2.3 校验位的产生 1、LDPC编码基础知识 LDPC属于线性分组码,线性分组码的基本知识如下: 编码后的码字是由初始二进制序列与生成矩阵在二进制域相乘后得到,生成矩阵与校验矩阵,校验矩阵与编码后…

OpenVINO使用教程--resnet分类模型部署

OpenVINO使用教程--resnet分类模型部署 本节内容模型准备推理测试分析&总结本节内容 OpenVINO 根据AI技术类型将部署任务分成传统模型模型部署和生成式AI模型部署,传统模型指的是各种CNN小模型,这部分部署只需要OpenVINO包,具体安装教程可以参考之前的章节:OpenVINO环境…

无字母数字webshell的命令执行

在Web安全领域&#xff0c;WebShell是一种常见的攻击手段&#xff0c;通过它攻击者可以远程执行服务器上的命令&#xff0c;获取敏感信息或控制系统。而无字母数字WebShell则是其中一种特殊形式&#xff0c;通过避免使用字母和数字字符&#xff0c;来绕过某些安全机制的检测。 …

C++斯特林数在C++中的数学理论与计算实现1

一、 斯特林数概述 1.1 组合数学中的核心地位 斯特林数&#xff08;Stirling Numbers&#xff09;是组合数学中连接排列、组合与分划问题的核心工具&#xff0c;分为两类&#xff1a; 第一类斯特林数&#xff08;Stirling Numbers of the First Kind&#xff09;&#xff1a…

[C++] STL大家族之<map>(字典)容器(附洛谷)

map-目录 使用方法头文件与声明定义基本操作 使用方法 头文件与声明定义 头文件是: #include <map>我们这样声明一个字典: map</*key_type*/, /*value_type*/> /*map_name*/; // 例子: map<int, char> mp;这里稍作解释: key_type是你每个键值对中的键的…

使用 Flutter 在 Windows 平台开发 Android 应用

以下是完整的开发流程&#xff0c;包括环境搭建、代码实现和应用发布&#xff0c;帮助你开发一个具有地图显示、TCP 通信功能的 Android 应用。 一、环境搭建 1. 安装 Flutter SDK 从 Flutter 官网 下载最新稳定版 SDK解压到本地目录&#xff08;如 D:\flutter&#xff09;添…

【模板】埃拉托色尼筛法(埃氏筛)

一、算法简介 在数论与编程竞赛中&#xff0c;求解 [ 1 , n ] [1,n] [1,n] 范围内的所有质数是常见的基础问题。埃拉托色尼筛法&#xff08;Sieve of Eratosthenes&#xff09; 是一种古老而高效的算法&#xff0c;可以在 O ( n log ⁡ log ⁡ n ) O(n \log \log n) O(nlogl…

AI Agent实战 - LangChain+Playwright构建火车票查询Agent

本篇文章将带你一步步构建一个智能火车票查询 Agent&#xff1a;你只需要输入自然语言指令&#xff0c;例如&#xff1a; “帮我查一下6月15号从上海到南京的火车票” Agent就能自动理解你的需求并使用 Playwright 打开 12306 官网查询前 10 条车次信息&#xff0c;然后汇总结果…

RabbitMQ的交换机和队列概念

&#x1f3ea; 场景&#xff1a;一个外卖平台的后台系统 假设你开了一家在线外卖平台&#xff1a; 饭店是消息的生产者&#xff08;Producer&#xff09;顾客是消息的消费者&#xff08;Consumer&#xff09;你开的外卖平台就是RabbitMQ消息系统 &#x1f501; 第一部分&…

德国马克斯·普朗克数学研究所:几何朗兰兹猜想

2025年科学突破奖 4月5日在美国洛杉矶揭晓&#xff1a;数学突破奖&#xff1a;德国马克斯普朗克数学研究所&#xff1a;几何朗兰兹猜想 德国马克斯普朗克数学研究所&#xff08;Max Planck Institute for Mathematics, MPIM&#xff09;在几何朗兰兹猜想的研究中扮演了核心角色…

TerraFE 脚手架开发实战系列(一):项目架构设计与技术选型

TerraFE 脚手架开发实战系列&#xff08;一&#xff09;&#xff1a;项目架构设计与技术选型 前言 在前端开发中&#xff0c;项目初始化往往是一个重复且繁琐的过程。每次新建项目都需要配置 webpack、安装依赖、设置目录结构等&#xff0c;这些重复性工作不仅浪费时间&#…

准确--CentOS 7.9在线安装docker

一、安装Docker前的准备工作 操作系统版本为CentOS 7.9&#xff0c;内核版本需要在3.10以上。确保能够连通互联网&#xff0c;为避免网络异常&#xff0c;建议关闭Linux的防火墙&#xff08;生产环境下请根据实际情况设置防火墙出入站规则&#xff09;。 # 查看内核版本 sudo…