首页 > 编程语言 >Bellman-Ford算法实现带有负权边的单源最短路

Bellman-Ford算法实现带有负权边的单源最短路

时间:2024-01-01 12:11:08浏览次数:37  
标签:dist int 短路 单源 Bellman Ford 顶点 dp

Bellman-Ford算法

对于Dijkstra算法,不妨给出这样一个例子

graph LR A((A)) -->|1| C((C)) A -->|2|D((D)) D -->|-4| C

根据Dijkstra算法的流程,选取A为源点。更新与A邻接的顶点,有C和D。选取已更新顶点中距离A的最小值,显然选择边权为1的边所连接的顶点C,并将C收入最短路集合S中,此时已经确定A->C的最短路为1。那么问题就出现了。
由于已经收入S中的顶点都视为最短路上的顶点且不可更改,因此由Dijkstra算法确定的A->C的最短路就是1。但是显然实际的最短路是A->D->C,花销为-2。

这时我们就需要使用别的方法来求出其最短路。比如Bellman-Ford以及其使用队列优化后的SPFA

Bellman-Ford的实现

Bellman-Ford算法实际上采用动态规划的思想。首先大前提是一个结论,对于有n个顶点的图,从源点到达其他任意顶点最多经过n-1条边。
定义\(dp[i][j]\),代表最多经过\(i\)条边到达顶点\(j\)的最小花销
显然\(dp[0][j] = +\infty\)
对于给定\(dp[i][j]\),考虑其最小花销,两个方面。

  1. 考虑从前一个顶点 k 到达顶点 j ,\(dp[i][j] = dp[i-1][k] + w[k->j]\)
  2. 考虑不经过新的中间顶点(即维持原状) ,\(dp[i][j] = dp[i - 1][k]\)

状态转移方程为\(dp[i][j] = min(dp[i - 1][j] , dp[i - 1][k] + w[k ->j])\)
显然在状态转移时,更新本层的状态只用到了上一层的状态,不妨加一个滚动数组优化,使用一维数组即可。

最终的代码如下
配合例题853. 有边数限制的最短路 - AcWing题库

#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e5 + 20;
int n, m, k;
struct Edge
{
	int v;
	int u;
	int w;
}edges[N];
int dist[N],last[N];
void bellman_ford()
{
	memset(dist, 0x3f, sizeof dist);
	dist[1] = 0;
	for (int i = 0; i < k; i++)//这里k的含义就是经过不超过k条边的最短路
	{
		memcpy(last, dist, N); //last保存上一层的状态。
		for (auto x : edges)
		{
			dist[x.v] = min(dist[x.v], last[x.u] + x.w);//更新使用上一层的状态来更新
		}
	}
}
int main()
{
	cin >> n >> m >> k;
	for (int i = 0; i < m; i++)
	{
		int v, u, w;
		cin >> v >> u >> w;
		edges[i] = { v,u,w };
	}
	bellman_ford();
	if (dist[n] > 0x3f3f3f3f / 2)
		puts("impossible");
	else
		cout << dist[n];
	return 0;
}

这里使用last数组记录上一层的状态。可以看出,当要求求出最多经过k条边的最短路时,只可以使用bellman-ford来做。同时由于动态规划更新状态不需要有序,因此可以简化图,只存储边

使用Bellman-Ford判断负值圈

所谓负值圈就是一个权值和为负数的环,如下

flowchart LR A((A)) -->|2| B((B)) B -->|-5| C((C)) C -->|1| A

-5 + 2 + 1 = -2,这就形成了一个负环。
存在负值圈的图一定无最短路
但是使用Bellman-Ford算法可以检测出图中是否有负值圈。
使用一个数组cnt[N]维护更新到第N个节点所经过的边数。由于从源点到达其他任意顶点最多经过n-1条边,但是对于有负值圈的图,由于总是会有更小的路径,因此其经过的边数会大于n。我们仅需要检测在某次更新中cnt[i]是否会大于n-1即可

标签:dist,int,短路,单源,Bellman,Ford,顶点,dp
From: https://www.cnblogs.com/CrescentWind/p/17938567

相关文章

  • G. Bicycles 分层图单源最短路
    题目链接简单描述一下题意:给定n个点,m条带权无向边,每个点i有一辆速度系数为Si的自行车。每经过一个点即可拥有该点的自行车,在任意两点之间路过的消耗为:已经拥有的某辆自行车的速度Si*边权Wi,求从1号点到n号点的最小消耗。思路:因为需要求的是最小的总消耗,所以在某个点出发时,我......
  • Dijkstra实现单源最短路
    Dijkstra算法求单源最短路Dijkstra算法应用于求一个给定图的单个源点到其他各顶点的最短路。其中应用Dijkstra算法的图应满足如下条件图中没有负权边有向或者无向图都可以图中若有自环或者重边也可以(需要自己先筛选一下)Dijkstra算法的核心就是从源点开始对各个顶点进行......
  • Bellman-Ford Algorithm 算法
    一、处理问题:负权值有向图单原点最短路径问题二、算法描述:假设带权值有向图中没有包含负权值环。定义一个距离数组,dist[0...n-1],dis[i]表示从原点到i的最短路径值初始化数组,假设一开始在原点src出发,终点为dst,那么dist[src]=0遍历所有的有向边,当前遍历边(a,b),a->b,权值为c,那么......
  • 7-5 单源最短路径
    7-5单源最短路径请编写程序求给定正权有向图的单源最短路径长度。图中包含n个顶点,编号为0至n-1,以顶点0作为源点。输入格式:输入第一行为两个正整数n和e,分别表示图的顶点数和边数,其中n不超过20000,e不超过1000。接下来e行表示每条边的信息,每行为3个非负整数a、b、c,其中a和b表示......
  • class065 A星、Floyd、Bellman-Ford与SPFA【算法】
    class065A星、Floyd、Bellman-Ford与SPFA【算法】2023-12-919:27:02算法讲解065【必备】A星、Floyd、Bellman-Ford与SPFAcode1A*算法模版//A*算法模版(对数器验证)packageclass065;importjava.util.PriorityQueue;//A*算法模版(对数器验证)publicclassCode01_AStarAlgori......
  • 《convex optimization》——Stanford University open class
    202312151.Introductionmathematicaloptimizationleast-squaresandlinearprogramingconvexoptimizationexapmlecoursegoalsandtopicsnonlinearoptimizationbriefhistoryofconvexoptimizationmathmeticaloptimizationoptimizationproblemminimize......
  • #P1009. 单源最短路
    模板代码#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=1e5+10;vector<pair<int,int>>a[N];intdis[N],vis[N];intn,m;voidspfa(){ for(inti=1;i<=n;i++)dis[i]=0x3f; queue<int>q; dis[1]=0; q.push(......
  • P3371 【模板】单源最短路径
    【模板】单源最短路径(标准版)题目背景2018年7月19日,某位同学在NOIDay1T1归程一题里非常熟练地使用了一个广为人知的算法求最短路。然后呢?\(100\rightarrow60\);\(\text{Ag}\rightarrow\text{Cu}\);最终,他因此没能与理想的大学达成契约。小F衷心祝愿大家不再......
  • bellman_ford算法
    Bellman–Ford算法是一种基于松弛(relax)操作的最短路算法,可以求出有负权的图的最短路,并可以对最短路不存在的情况进行判断。有边数限制的最短路普通做法intne[N],h[N],idx,e[N],wt[N];//wt[]表示边权voidadd(intu,intv,intw)//链式前向星存图{idx++;......
  • Struct ForDemo03
    packagecom.chen.struct;publicclassForDemo03{publicstaticvoidmain(String[]args){//练习2:用while或for循环输出1-1000之间能被5整除的数,并且每行输出3个for(inti=0;i<100;i++){if(i%5==0){System.out.p......