当前位置:   article > 正文

【图论】Bellman_Ford算法求有步数限制的最短路(图文详解)_有限制的最短路

有限制的最短路

一、前言

在之前的学习中,我们学习了用Dijkstra算法求有向图的最短路问题Dijkstra算法求最短路博客。但是在Djkstra算法中,我们认为可以走的步骤是无限的,但是在日常问题中,我们需要解决有步数限制的最短路问题,这个时候,我们就需要学习一个新的算法来解决这个问题----Bellman_Ford算法。虽然看上去又是一个高大上的算法,但其实这个算法的实现也并不是什么太难的事情。而且,它还能处理边的长度为负数的情况。

二、Bellman_Ford算法介绍

由于是有步数限制和负数边存在,我们不能使用Djkstra算法,但是Bellman_Ford算法的实现和Djkstra算法在思路上还是差不多的。

首先,初始化距离:起点到起点的距离为0,到其它点的距离都为无限大(红字代表到起点的距离,蓝字代表边的长度)
在这里插入图片描述
Bellman_Ford算法首先需要枚举步数,然后更新第i步后每个点距离起点的最短长度。具体实现方法是两层for循环,第一层循环步数,而第二层循环枚举每一条边a------>b长为w,比较当前的dis[b]与dis[a]+w,更新最小值。但是这里要注意,我们每次需要用一个数组te[N]来存下dis[N]用来下一步的更新,因为在算法公式中“比较当前的dis[b]与dis[a]+w,更新最小值”的操作我们需要用到dis,但是在每一次循环中,dis是不断更改的,因此我们需要用te数组存下上一次变化后的结果。

1.如图。第一次循环步数后,走完第一步后图变为
dis={0,1e9,1e9,1e9,1e9};
te={0,1e9,1e9,1e9,1e9};
在这里插入图片描述
1e9>te[1]+2=2,dis[2]=2;
dis={0,2,1e9,1e9,1e9};

2.继续循环到走二步,图变为
dis={0,2,1e9,1e9,1e9};
te={0,2,1e9,1e9,1e9};
在这里插入图片描述
1e9>te[2]-2=0;dis[3]=0
1e9>te[2]+1=3;dis[4]=3
dis={0,2,0,3,1e9};

3.走三步
dis={0,2,0,3,1e9};
te={0,2,0,3,1e9};
在这里插入图片描述
3>te[3]+2=2;dis[4]=2
1e9>te[4]+1=4;dis[5]=4
dis={0,2,0,2,4};

4.走四步
dis={0,2,0,2,4};
te={0,2,0,2,4};
在这里插入图片描述
4>te[4]+1=3;dis[5]=3
dis={0,2,0,2,5};

在上面的有向图中,最长能走4步,然后在上面的图中我们就枚举了步数为1~4步时的到各个点的最短的距离。

二、Bellman_Ford代码实现

例题链接:Acwing 有边数限制的最短路

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。

请你求出从 1 号点到 n 号点的最多经过 k 条边的最短距离,如果无法从 1 号点走到 n 号点,输出 impossible。

注意:图中可能 存在负权回路 。

输入格式
第一行包含三个整数 n,m,k。

接下来 m 行,每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。

输出格式
输出一个整数,表示从 1 号点到 n 号点的最多经过 k 条边的最短距离。

如果不存在满足条件的路径,则输出 impossible。

数据范围
1≤n,k≤500,
1≤m≤10000,
任意边长的绝对值不超过 10000。

输入样例:
3 3 1
1 2 1
2 3 1
1 3 3
输出样例:
3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

这一道题就是Bellman_Ford算法的板子题,是需要看懂的,不过这里我们并不需要用到我们存图常用的链式前向星存图,因为我们只要遍历了所有的边就可以了,并没有要求要怎样遍历完所有的边。

看懂代码前你需要知道的
1.作者重写的mem函数“#define mem(a,b) memset(a,b,sizeof(a))”
2.memcpy函数

//#pragma GCC optimize(2)
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<ctime>
#include<cstring>
#include<list>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef  pair<int, int> PII;
const int N = 1e6 + 7;

int n, m, k;
int dis[N], te[N];
struct edge  //边
{
	int f, t, w;   //定义边的起点,终点,长度
};

edge ed[N];

int Bellman_Ford()
{
	mem(dis, 0x3f);  //初始化距离为一个很大的数
	dis[1] = 0;  //起点到起点的距离为0
	for (int i = 0; i < k; i++)  //遍历步数
	{
		memcpy(te, dis, sizeof dis);   //复制数组函数
		for (int j = 1; j <= m; j++)   //遍历每一条边
		{
			int f = ed[j].f, t = ed[j].t, w = ed[j].w;
			dis[t] = min(dis[t], te[f] + w);
		}
	}
	return dis[n];  //返回最后的答案
}

void solve()
{
	cin >> n >> m >> k;
	for (int i = 1; i <= m; i++)  //输入每一条边
	{
		int a, b, c;
		cin >> a >> b >> c;
		ed[i] = { a,b,c };
	}
	int t=Bellman_Ford();  //用一个数来接收最后的答案

	if (t > INF / 2)     //如果这个数很大代表到不了,至于为什么不是">INF"在下方解释
		cout << "impossible" << endl;
	else   //输出答案即可
		cout <<t << endl;
}

int main()
{
	//std::ios::sync_with_stdio(false);
	//cin.tie(0), cout.tie(0);
	solve();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

解释为什么不是">INF"而是“>INF/2”:由于题目中可能会有负数边存在,在公式"dis[t] = min(dis[t], te[f] + w);“中,如果dis[t]=1e9,w为-2,那么我们发现dis[t]被更新成了1e9-2,但是1e9-2实际上并不是代表路的长度为1e9-2,而是代表不能到达,根据题目中数据范围,我们可以发现,我们虽然不能写成”>INF",但是“>INF/2”是可以判断是否能到达终点的。

作者:Avalon Demerzel,喜欢我的博客就点个赞吧,更多图论与数据结构知识点请见作者专栏《图论与数据结构》

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
  

闽ICP备14008679号