单源最短路径问题
- 给定 有向图。(无向图也适用,他是特殊有向图么)。
- 节点是连续的整数编号
- (x,y,z) x-> y 是有向边,长度为 z 长度为 1 的话,我们选择 BFS 算法,非1 BFS 可能会出错
- 1号起点
- dist[1...n] 表示 1 到每一个节点最短的路径。
Bellman-Ford 算法 (基于 dp 和 迭代 思想)
算法流程
- for所有边,
dist[y] = min(dist[y], dist[x] + z ),x.y 含义是 x到y的一条有向边 x起点,y终点。 - 重复 n 次, 优化 : (没有更新就跳出)
- 权值 :可以解决权值为负数的情况
- 有解的情况 :图中没有负环
- 适用场景 :擅长解决有边数限制的最短路问题。判断负环。
- 时间复杂度 : O(n*m)。
- 经过最多 k 条边的最短路径问题,这种问题只能使用Bellman-Ford算法。
算法实现
题目来自 acwing 有边数限制的最短路
import java.util.*;
import java.io.*;
public class Main {
static int INF = 0x3f3f3f3f;
public static void main(String[] args) {
Scanner sc = new Scanner(new BufferedInputStream(System.in));
int n = sc.nextInt();
int m = sc.nextInt();
int k = sc.nextInt();
// edg二维[0][1][2] 分别表示 起点 x 终点 y 和 边权 z
int[][] edg = new int[m + 1][3];
int[] dist = new int[n + 1];
int[] back = new int[n + 1];
for (int i = 1 ; i <= n ; i ++) {
dist[i] = INF;
}
// 起点距离初始化为0
dist[1] = 0;
// 初始化图
for (int i = 0 ; i < m ; i ++) {
edg[i][0] = sc.nextInt();
edg[i][1] = sc.nextInt();
edg[i][2] = sc.nextInt();
}
for (int i = 0 ; i < k ; i ++) {
boolean flag = false;
back = Arrays.copyOf(dist,n + 1);
for (int[] edge : edg) {
int x = edge[0];
int y = edge[1];
int z = edge[2];
// dist[y] = Math.min(dist[x] + z, dist[y]);
if (dist[y] > back[x] + z) {
dist[y] = back[x] + z;
flag = true;
}
}
if (!flag) break;
}
if (dist[n] > INF / 2) System.out.println("impossible");
else System.out.println(dist[n]);
}
}
Dijkstar 算法
算法流程
- 初始化 dist[i] = 0 , i 为起点。
- 找出未被解锁的、dist[i]最小的点,解锁该点。第一次 为 1.
- 扫描该点所有出边,dist[y] = (dist[y],dist[x] + z)
- 直到全部被解锁。即可。
- 应用场景 : 单元,超级源点,边的权值没有负数
- 优化 : 优先级队列每次取最小的点。
- 时间复杂度 O(m * log(n))
算法实现
import java.util.*;
import java.io.*;
public class Main {
static int INF = 0x3f3f3f;
public static void main(String[] args) throws IOException {
BufferedReader read = new BufferedReader(new InputStreamReader(System.in));
PrintWriter wt = new PrintWriter(new OutputStreamWriter(System.out));
String[] s = read.readLine().split(" ");
int n = Integer.parseInt(s[0]);
int m = Integer.parseInt(s[1]);
// 邻接矩阵存储
int[][] g = new int[n + 1][n + 1];
for (int i = 1; i <= n ; i ++) {
for (int j = 1; j <= n ; j ++)
g[i][j] = INF;
g[i][i] = 0;
}
while (m -- > 0) {
s = read.readLine().split(" ");
int x = Integer.parseInt(s[0]);
int y = Integer.parseInt(s[1]);
int z = Integer.parseInt(s[2]);
g[x][y] = Math.min(g[x][y],z);
}
int[] dist = new int[n + 1];
Arrays.fill(dist,INF);
dist[1] = 0;
boolean[] st = new boolean[n + 1];
// 遍历 n 个点
for (int i = 1 ; i <= n ; i ++) {
int t = -1;
for (int j = 1; j <= n ; j ++) {
if (!st[j] && (t == -1 || dist[t] > dist[j])) {
t = j;
}
}
st[t] = true;
// 找到了 最小的 t 更新距离
for (int j = 1; j <= n ; j ++) dist[j] = Math.min(dist[j] , dist[t] + g[t][j]);
}
if (dist[n] == INF) wt.println(-1);
else wt.println(dist[n]);
wt.flush();
wt.close();
read.close();
}
}
堆优化版dijstra
import java.util.*;
import java.io.*;
public class Main {
static final int INF = (int)1e9 + 10;
public static void main(String[] args) {
Scanner sc = new Scanner(new BufferedInputStream(System.in));
int n = sc.nextInt();
int m = sc.nextInt();
boolean[] v = new boolean[n + 1];
int[] dist = new int[ n + 1];
List<List<Integer>> edg = new ArrayList<>();
List<List<Integer>> val = new ArrayList<>();
for (int i = 0 ; i <= n ; i ++) {
edg.add(new ArrayList<>());
val.add(new ArrayList<>());
dist[i] = INF;
}
while (m -- > 0) {
int x = sc.nextInt();
int y = sc.nextInt();
int z = sc.nextInt();
edg.get(x).add(y);
val.get(x).add(z);
}
PriorityQueue<int[]> queue = new PriorityQueue<>((a,b) -> {return a[1] - b[1];});
queue.offer(new int[]{1,0});
dist[1] = 0;
while (!queue.isEmpty()) {
int[] top = queue.poll();
int x = top[0];
if (v[x]) continue;
v[x] = true;
for (int i = 0 ; i < edg.get(x).size() ; i ++ ) {
int y = edg.get(x).get(i);
int z = val.get(x).get(i);
if (dist[y] > dist[x] + z) {
dist[y] = dist[x] + z;
queue.offer(new int[]{y,dist[y]});
}
}
}
System.out.println(dist[n] == INF ? -1 : dist[n]);
}
}