【蓝桥杯必胜】试题 算法训练 最短路-Bellman-ford算法

本文介绍了如何使用Bellman-Ford算法解决含有负权重的有向图中最短路径问题,详细解释了算法步骤,并提供了Java实现代码。尽管此算法在数据规模较大时可能会超时,但在没有负权环的情况下仍能正确找到最短路径。文章还提及了SPFA算法作为优化的选择。
摘要由CSDN通过智能技术生成

文章目录

题目

资源限制
时间限制:1.0s 内存限制:256.0MB

问题描述

给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环)。请你计算从1号点到其他点的最短路(顶点从1到n编号)。

输入格式

第一行两个整数n, m。

接下来的m行,每行有三个整数u, v, l,表示u到v有一条长度为l的边。

输出格式

共n-1行,第i行表示1号点到i+1号点的最短路。

样例输入

3 3
1 2 -1
2 3 -1
3 1 2

样例输出

-1
-2

数据规模和约定

对于10%的数据,n = 2,m = 2。
对于30%的数据,n <= 5,m <= 10。
对于100%的数据,1 <= n <= 200001 <= m <= 200000-10000 <= l <= 10000,保证从任意顶点都能到达其他所有顶点。

解析

根据题目描述,单源最短路径问题,Floyd 算法容易超时,Dijkstra 算法只适用权重为正的图,所以这里采用了更具有普适性的 Bellman-ford 算法

Bellman-ford 算法适用于单源点最短路径的问题,且可以处理边的权重为负的图。通过松弛函数计算源点到其他所有点的最短距离,缺点是时间复杂度较高,有待优化,且不能处理负环

可以阅读司徒正美大佬写的博客 Bellman-ford 算法

算法步骤:

  • 初始化所有点。每个点保存一个值,表示从源点到达该点的距离,源点值设为0,其他的值设为无穷大即代表不可达。
  • 开始循环。循环下标从1到 n-1(n为图中点的个数),循环体遍历所有的边,进行松弛计算。
  • 遍历图中所有的边(edge(u, v)),判断是否存在:如果 d(v) > d(u) + w(u, v),则返回 false,表示图中存在从源点可达的权为负的回路,即负权环。

拓展:关于对 Bellman-ford 算法的优化,请参见 SPFA 算法,大家自行查阅资料。

代码

// 试题 算法训练 最短路
import java.util.Scanner;

public class Main {
	
	// 保存结果,单源点到其他点的最短路径
	public static long[] distance;
	
	// 声明一个内部类,保存边的信息
	static class edge {
		public int a; // 起点
		public int b; // 终点
		public int weight; // 权重
		
		edge(int a, int b, int weight) {
			this.a = a;
			this.b = b;
			this.weight = weight;
		}
	}
	
	// 求源点到其他所有顶点之间的最短距离
	public static void bellmanFord(int n, edge[] E) {
		distance = new long[n+1];
		// 1. 初始化。这里害怕下标搞错了,将源点下标规定为1,其他初始化为 Integer.MAX_VALUE
		for (int i=2; i<=n; i++) {
			distance[i] = Integer.MAX_VALUE;
		}
		// 2. 松弛操作。
		for (int i=0; i<n-1; i++) {
			boolean flag = false;
			for (int j=0; j<E.length; j++) {
				// System.out.println("当前E[j]:" + E[j].a + " " + E[j].b + " " + E[i].weight);
				if (distance[E[j].a] != Integer.MAX_VALUE && distance[E[j].b] > distance[E[j].a] + E[j].weight) {
					distance[E[j].b] = distance[E[j].a] + E[j].weight;
					// System.out.println("更新distance[]:" + distance[E[j].a] + " " + distance[E[j].b] + " " + E[j].weight);
					flag = true;
				}
			}
			// 若遍历了所有边后,distance 没有更新,则提前退出循环结束算法
			if (!flag) {
				break;
			}
		}
		// 3. 判定是否存在负环。若遍历了所有边后,distance 还存在更新,则说明有负环。这里因为题目做了约束保证没有负环,害怕卡时间就去掉了
//		boolean judge = true;
//		for (int i=2; i<=n; i++) {
//			if (distance[E[i].b] > distance[E[i].a] + E[i].weight) {
//				judge = false;
//				break;
//			}
//		}
//		return judge;
	}
	
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		
		int n = input.nextInt();
		int m = input.nextInt();
		edge[] E = new edge[m];
		
		for (int i=0; i<m; i++) {
			int u = input.nextInt();
			int v = input.nextInt();
			int weight = input.nextInt();
			E[i] = new edge(u, v, weight);
		}
		
		bellmanFord(n, E);
		
		for (int i=2; i<distance.length; i++) {
			System.out.println(distance[i]);
		}

		input.close();
	}
}

差不多也是超时边缘了哈,有时间可以用SPFA 算法试试。
在这里插入图片描述
如果我的博客对你有用的话,请给一个一键三连吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风落_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值