一、题目描述
给定一个 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:为什么正无穷要写成
,而不是
Integer.MAX_VALUE
?
A3:参考这里
复杂度分析
- 时间复杂度: ,
- 空间复杂度: ,
参考链接:
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