【图论】C005_有边数限制的最短路(Bellman-Ford)

一、题目描述

给定一个 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

二、题解

方法一:Bellman-Ford 简单应用

无论是否有负权的图,BellmanFord 算法对所有结点松弛 V-1 次,就可以得到起点到其它点的最短距离。这里有边数限制 k,问题转化为松弛 k 次看是否能够找到源点到其它点的最短路。

因为这里不需要寻找最短的边只需要更新 dist 与图的边,所以不需要将图的存储写得那么复杂,直接用 Node[MAXN] 存储边的信息即可。

import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static int V, E, k;
	static Node[] nodes;
	static int[] dist;
	static int INF = (int)1e9+5;
	static int[] backup = new int[5000];	///只使用上一次更新的状态,避免串联更新,eg:更新过1到2距离,然后利用另一个条件又更新了一遍
	private static int bellmanFord() {
		Arrays.fill(dist, INF);
		dist[1] = 0;
		for (int i = 0; i < k; i++) {		//更新k次,说明from到to最多不超过k条边
			backup = Arrays.copyOf(dist, V+1);
			for (int j = 1; j <= E; j++) {
				int from = nodes[j].from;
				int to = nodes[j].to;
				int cost = nodes[j].cost;
				dist[to] = Math.min(dist[to], backup[from] + cost);
			}
		}
		if (dist[V] > INF / 2)		//可能存在负权边
			return -1;
		return dist[V];
	}
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
        
		V = sc.nextInt();
		E = sc.nextInt();
		k = sc.nextInt();
		dist = new int[V+1];
		nodes = new Node[V+1];
		for (int i = 1; i <= E; i++) {
			nodes[i] = new Node();
			nodes[i].from = sc.nextInt();
			nodes[i].to = sc.nextInt();
			nodes[i].cost = sc.nextInt();
		}
		int res = bellmanFord();
		if (res == -1)	System.out.println("impossible");
		else System.out.println(dist[V]);
    }
	static class Node {
		int from, to;
		int cost;
		public Node() {}
		public Node(int from, int to, int cost) {
		  this.from = from;
		  this.to = to;
		  this.cost = cost;
		}
	}
}

可能存在的疑惑:

  • Q1:为什么使用 int[] backup 数组呢?
    A1:因为一般的最短路问题中 int[] dist 数组的更新是串联的,我们只使用上一轮的状态是指有边数限制的情况下,我们尽量走边少的。
  • Q2:我 if 条件写成 if (dist[V] > INF / 2) 呢,不应该是 if (dist[V] > INF)return -1 吗?
    A2:因为题目中说可能存在负权边,所以就算到达不了第 n 个结点,条件 dist[V] > INF 也会成立。
  • Q3:为什么正无穷要写成 ( i n t )   1 e 9 + 5 (int)\ 1e9+5 ,而不是 Integer.MAX_VALUE
    A3:参考这里

复杂度分析

  • 时间复杂度: O ( V 2 ) O(V^2)
  • 空间复杂度: O ( V 2 ) O(V^2)

参考链接:
https://blog.csdn.net/powermod0927/article/details/104826169
https://blog.csdn.net/qq_30277239/article/details/101030111
https://www.cnblogs.com/qdu-lkc/p/12255492.html

发布了691 篇原创文章 · 获赞 151 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/105475498