最短路之 Bellman-ford 算法

这篇具有很好参考价值的文章主要介绍了最短路之 Bellman-ford 算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

bellman-ford算法的思想 :

若有向图有n个点,m条边 。 扫描所有边,对每条边进行一次松弛(即对a,b为端点 , 权重为w的边,dist[b] = min(dist[a] , dist[a] + w )) 重复此流程(最多重复n次)直到没有更新操作发生


例题1 bellmanford板子

给你一张 n 个顶点 m 条边的有向简单图,顶点编号从 1 到 n,每条边都有一个边权,边权为非负整数。

现在有 k 组询问,每组询问读入两个整数 x,y 请求出从 x 号点到 y 号点的最短路的长度。如果不存在从 x 号点到 y 号点的路径,请输出 -1。

输入格式

第一行三个整数 n,m,k表示图的顶点数、边数和询问次数。

接下来 m𝑚 行,每行三个整数 x,y,z表示 x𝑥 号点到 y 号点有一条边权为 z 的有向边。

接下来 k 行,每行两个整数 x,y表示一组询问。

输出格式

输出共 k 行,每行一个数表示一组询问的答案。

样例输入

3 3 2
1 2 3
2 3 2
3 2 1
1 3
3 1
样例输出

5
-1
数据规模

对于所有数据,保证 2≤n≤5000,0≤m≤10000,1≤k≤5,1≤x,y≤n,1≤z≤100002≤n≤5000,0≤m≤10000,1≤k≤5,1≤x,y≤n,1≤z≤10000。


我们可以发现K非常小,那么我们可以对每次询问都做一次bellman - ford算法,求出dist[y]


代码


# include<bits/stdc++.h>
 
using namespace std;
 
const int M = 1e4+10,N = 5010;	//N根据点的数据范围定的  M根据边
 
struct edge
{
	int x,y,w;  //起点,终点,边权
}edge[M];		//建立一个数组装所有边
 
int dist[N];	//每个点距离初始点的距离
 
int main()
{
	int n,m,k;cin>>n>>m>>k;
	for(int i=0;i<m;i++)
	{
		cin>>edge[i].x>>edge[i].y>>edge[i].w; 
	}
	while(k--)	//进行k次bellman - ford算法
	{
		int x,y;scanf("%d%d",&x,&y);
		memset(dist,0x3f,sizeof(dist));	//每次都要先更新dist数组,将起点的dist设置为0
		dist[x] = 0;
		while(1)	
		{
			bool flag = false;	//只要dist更新,flag就会被置为true
			for(int i=0;i<m;i++) 
			{
				int a = edge[i].x,b = edge[i].y,d = edge[i].w; 
				if(dist[b] > dist[a] + d)	//遍历每条边进行松弛操作
				{
					flag = true;
					dist[b] = dist[a] + d;
				}
			}
			if(!flag) break;	       //只要不在更新,则bellman-ford算法结束
		}
		if(dist[y] > 0x3f3f3f3f/2) cout<<"-1"<<endl;	//因为每条边都会进行松弛所以即使走不到dist的值也不再是0x3f3f3f3f
		else cout<<dist[y]<<endl;
		
	}
	
	return 0;
}

例题2

小蜗要在城市中租房子,他想要找到一处合适的住址,使得住址到商场、工作地点、医院的距离和最小。城市可以抽象为一张 n 个点 m 条边的无向简单图,顶点编号从 1 到 n,每条边都有一个边权,边权为非负整数。

输入格式

第一行两个整数 n,m,表示图的顶点数和边数。

接下来 m𝑚 行,每行三个整数 x,y,z表示 x 号点和 y 号点之间有一条边权为 z 的边。

接下来一行三个整数 a,b,c分别表示商场、工作地点、医院所在的顶点的编号。

输出格式

你需要寻找一个合适的住址(住址必须为 n 个顶点中的某个点,这个点可以和 a,b,c 重合),并输出一行一个整数表示住址到商场、工作地点、医院的最小距离和。

样例输入

4 3
1 2 1
2 3 1
3 4 1
1 2 4
样例输出

3
数据规模

对于所有数据,保证 3≤n≤5000,0≤m≤10000,1≤x,y,a,b,c≤n,1≤z≤10000 保证整张图连通并且 a,b,c两两不同。

我们可以先想一个最无脑的做法 :
遍历n个点,对每个点做一遍bellman-ford,每次算出这个点到a,b,c的和的最小值并记录,最后看最小值对应的是哪个点,复杂度是o(n²m) 如果这样子最差需要操作1e10次,会超时。

那我们不妨先对a,b,c各做一次bellman-ford,然后遍历n个点找到距离和的最小值,这样子复杂度就变成了o(nm+n)


代码


# include<bits/stdc++.h>
 
using namespace std;
 
const int M = 1e4+10,N = 5e3+10;
struct edge
{
	int x,y,w;
}edge[2*M+10];int cnt = 0;					//无向图,存边数组要开成2*M
 
int dist1[N],dist2[N],dist3[N],dist[N];		//前三个数组分别以a,b,c为原点
 
int n,m;
int a,b,c;
void bellmanford(int x)
{
	memset(dist,0x3f,sizeof dist);
	dist[x] = 0;
	while(true)
	{
		bool flag = false;
		for(int i=1;i<=cnt;i++)
		{
			int u = edge[i].x,v = edge[i].y,d = edge[i].w;
			if(dist[v]>dist[u]+d) 
			{
				flag = true;
				dist[v] = dist[u]+d;
			}
		}
		if(!flag) break;
	}
}
int main()
{
	cin>>n>>m;
	for(int i=0;i<m;i++) 
	{
		int x,y,z;
		cin>>x>>y>>z;
		edge[++cnt].x = x,edge[cnt].y = y,edge[cnt].w = z;	//无向图,起点和终点可以互换
		edge[++cnt].x = y,edge[cnt].y = x,edge[cnt].w = z; 
	}
	cin>>a>>b>>c;
	bellmanford(a);memcpy(dist1,dist,sizeof dist);	//每次将dist拷贝到各自dist数组
	bellmanford(b);memcpy(dist2,dist,sizeof dist);
	bellmanford(c);memcpy(dist3,dist,sizeof dist);
	int ans = 2e9;
	for(int i=1;i<=n;i++)	ans = min(ans,dist1[i]+dist2[i]+dist3[i]);
	cout<<ans<<endl;
	return 0;
}

例题3

小蜗想要完成一次说走就走的旅行,他来到了某旅行社进行咨询。

地图可以看做是一张 n 个顶点 m 条边的无向简单图,顶点编号从 1 到 n,每个顶点表示一座城市,在两个城市之间移动需要花费连接这两座城市的边的边权的代价。

小蜗是这家旅行社的vip用户,他拥有 k 张打折券,在一次移动中他可以使用一张打折券,使得这次移动的代价变成之前的一半,每条边只能使用一张打折券。

小蜗现在在 1 号城市,他想知道他最少需要花费多少代价可以到达 n 号城市,数据保证 1 号城市和 n 号城市两地连通。

输入格式

第一行三个整数 n,m,k 表示图的顶点数(也就是城市数)、边数和打折券的数量。

接下来 m 行,每行三个整数 x,y,z,表示 x 号城市和 y 号城市之间有一条边权为 z 的边。

输出格式

输出一行一个数表示答案。

样例输入

3 2 1
1 2 4
2 3 6
样例输出

7
数据规模

对于所有数据,保证 2≤n≤500,1≤m≤2000,1≤k≤10,1≤x,y≤n,1≤z≤10000 保证 z 是偶数。

我们可以设一个二维状态数组 f[i][j] 为 到第i个点用了j个打折券花费的最少代价

bellman-ford最本质的思想其实是一个点到另一个点的最短路径可以通过每个边松弛最多n次得到。那么我们应该也可以通过这种方法实现从一个状态到另一个状态。遍历每条边,起点x 终点y 权值w,那么状态一共有2*k-1种变化可能 即对于每个状态有不用代价卷和用代价卷两种可能,当代价卷用完了那只有不用代价卷这一种可能

最后的结果就是f[n][i] (i == 0,1,2...k) 的最小值
文章来源地址https://www.toymoban.com/news/detail-582264.html


代码


# include<bits/stdc++.h>
 
using namespace std;
 
const int M = 2e3 , N = 510;
 
struct edge
{
	int x,y,w;
}edge[2*M+10];
 
int cnt = 0;
int f[N][11];
 
int n,m,k;
 
void bellmanford(int a,int b)
{
	memset(f,0x3f,sizeof f);
	f[a][0] = 0;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<cnt;j++)
		{
			int x = edge[j].x,y = edge[j].y,w = edge[j].w;
			for(int l=0;l<=k;l++)
			{
				if(f[y][l]>f[x][l]+w)
				{
					f[y][l] = f[x][l]+w;	//不花费打折券
				}
				if(l<k && f[y][l+1]>f[x][l]+w/2)	
                //花费打折券,当i==k即没有打折卷可用的时候不执行
				{
					f[y][l+1] = f[x][l]+w/2;
				}
			}
		}
	}
}
 
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(int i=0;i<m;i++) 
	{
		int x,y,w;cin>>x>>y>>w;
		edge[++cnt].x = x;edge[cnt].y = y;edge[cnt].w = w;
		edge[++cnt].x = y;edge[cnt].y = x;edge[cnt].w = w;	
	}
	bellmanford(1,n);
	int ans = 0x3f3f3f3f;
	for(int i=0;i<=k;i++) ans = min(ans,f[n][i]);	//最小值即为答案
	cout<<ans<<endl;
	return 0;
}

到了这里,关于最短路之 Bellman-ford 算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 最短路径算法( Dijkstra + Bellman-Ford + SPFA + Floyd)

       文章目录 一、Dijkstra 算法 1、1 朴素版Dijkstra算法 1、1、1 Dijkstra求最短路 I 1、1、2 题解关键思路与与解答 1、2 堆优化版Dijkstra算法 1、2、1 Dijkstra求最短路 II 1、2、2 题解关键思路与答案 二、Bellman-Ford 算法 2、1 Bellman-Ford算法求有边数限制的最短路 2、1、1 题目描述 2、

    2023年04月08日
    浏览(37)
  • 【算法基础:搜索与图论】3.4 求最短路算法(Dijkstra&bellman-ford&spfa&Floyd)

    关于最短路可见:https://oi-wiki.org/graph/shortest-path/ 无向图 是一种 特殊的 有向图。(所以上面的知识地图上没有区分边有向还是无向) 关于存储:稠密图用邻接矩阵,稀疏图用邻接表。 朴素Dijkstra 和 堆优化Dijkstra算法的 选择就在于图 是 稠密的还是稀疏的。 算法步骤: 有一

    2024年02月16日
    浏览(45)
  • 最短路问题 Bellman-Ford(单源最短路径)(图解)

    对于边(u,v),用dist(u)和(u,v)的和尝试更新dist(v):                          dist(v) = min(dist(v) , dist(u)+l(u,v) 注:dist(i)为源点(起点)到i点的距离,l(u,v)为u-v的边权。 Bellman-Ford的基本操作是进行多次迭代,每一轮迭代对图上所有边进行松弛操作,直到

    2024年02月09日
    浏览(39)
  • 单源最短路径(spfa,Dijkstra, bellman-ford)

    目录  Dijkstra 原理:基于贪心。 为什么 Dijkstra 不能处理有负边的情况 Bellman-ford 原理:动态规划, 实质见floyd的另一篇博客 1,能找负环, 2,有变数限制的最短路径 spfa 原理 spfa怎么求负环, 原理:基于贪心。 第一步 初始化距离,dist[1] = 0, 一号点到起点的距离为0, 其他点

    2024年02月04日
    浏览(50)
  • 图论 - 最短路(Dijkstra、Bellman-Ford、SPFA、Floyd)

    单源:在边权正数时,稠密图用朴素Dijkstra,稀疏图用堆优化Dijkstra;存在负权边时,一般用SPFA,但是如果限制在k步内,则用Bellman-Ford。多源:只有Floyd,这个由于时间复杂度太高,在算法比赛中很少遇见。 1.问题描述 给定一个 n 个点 m 条边的有向图,图中可能存在重边和自

    2024年04月14日
    浏览(38)
  • Bellman-ford 贝尔曼-福特算法

    Bellman-ford算法可以解决负权图的单源最短路径问题 --- 它的优点是可以解决有负权边的单源最短路径问题, 而且可以判断是否负权回路 它也有明显的缺点,它的时间复杂度O(N*E)(N是点数 , E是边数)普遍是要高于Dijkstra算法O(N^2)的,像这里,我们使用邻接矩阵实现,那

    2024年02月06日
    浏览(44)
  • 图搜索算法详解 - DFS、BFS、Bellman-Ford、Dijkstra

    图搜索算法是许多应用程序的基础,例如社交网络分析、路径规划、数据挖掘和推荐系统。在本文中,我们将深入探讨图搜索算法的世界,探索它们的定义、重要性和实际应用。 图搜索算法是一种用于遍历图的技术,图是由 关系 连接的 节点集合 。在社交网络、网页或生物

    2024年02月16日
    浏览(43)
  • 算法基础复盘笔记Day06【搜索与图论】—— Dijkstra、bellman-ford、spfa、Floyd

    ❤ 作者主页:欢迎来到我的技术博客😎 ❀ 个人介绍:大家好,本人热衷于 Java后端开发 ,欢迎来交流学习哦!( ̄▽ ̄)~* 🍊 如果文章对您有帮助,记得 关注 、 点赞 、 收藏 、 评论 ⭐️⭐️⭐️ 📣 您的支持将是我创作的动力,让我们一起加油进步吧!!!🎉🎉 1. 题目

    2023年04月22日
    浏览(46)
  • 图论详解——Bellman-Ford(清晰易懂)

    开学第一周,晚上属实作业有点乱 于是就拖更了一周 今天我们来讲解一下图论最短路径算法中 最简单 最清晰易懂 同时时间复杂度最高的算法 它的时间复杂度能达到O(VE)(点的数量*边的数量) 在学习Bellman-Ford之前,你需要先学会链式前向星 大家可以上网或者其他途径自行

    2024年02月06日
    浏览(44)
  • C++ | 数据结构与算法 | 单源最短路径 | Dijkstra && Bellman Ford

    (关于代码实现的图结构,可以看图结构的实现这篇文章) Dijkstra的实现与Prim的实现相似,两者都是通过贪心思想实现,它们有什么不同呢?首先Prim算法是针对无向图的最小生成树的,而Dijkstra算法是针对 有向图 的最短路径生成。一个的目的是连接图中的所有顶点,生成一

    2024年02月03日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包