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
繁體地址,請注明出處:http://hk.pswp.cn/web/83753.shtml
英文地址,請注明出處:http://en.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…