当前位置:   article > 正文

代码随想录Day74(图论Part10)

代码随想录Day74(图论Part10)

94. 城市间货物运输| (Bellman_ford队列优化版 / SPFA)

题目:94. 城市间货物运输 I (kamacoder.com)

思路:

Bellman_ford 算法 每次都是对所有边进行松弛,其实是多做了一些无用功。

只需要对 上一次松弛的时候更新过的节点作为出发节点所连接的边 进行松弛就够了

因此,关键在于记录上次松弛更新过的节点,用队列来记录。

答案
  1. import java.util.*;
  2. class Edge {
  3. int to; // 链接的节点
  4. int val; // 边的权重
  5. Edge(int t, int w) {
  6. to = t;
  7. val = w;
  8. }
  9. }
  10. public class Main {
  11. public static void main(String[] args) {
  12. Scanner scanner = new Scanner(System.in);
  13. int n = scanner.nextInt(); // 顶点数
  14. int m = scanner.nextInt(); // 边数
  15. List<List<Edge>> graph = new ArrayList<>();
  16. for (int i = 0; i <= n; i++) {
  17. graph.add(new ArrayList<>());
  18. }
  19. // 将所有边保存起来
  20. for (int i = 0; i < m; i++) {
  21. int p1 = scanner.nextInt();
  22. int p2 = scanner.nextInt();
  23. int val = scanner.nextInt();
  24. // p1 指向 p2,权值为 val
  25. graph.get(p1).add(new Edge(p2, val));
  26. }
  27. int start = 1; // 起点
  28. int end = n; // 终点
  29. int[] minDist = new int[n + 1];
  30. Arrays.fill(minDist, Integer.MAX_VALUE);
  31. minDist[start] = 0;
  32. Queue<Integer> queue = new LinkedList<>();
  33. queue.offer(start); // 队列里放入起点
  34. while (!queue.isEmpty()) {
  35. int node = queue.poll();
  36. for (Edge edge : graph.get(node)) {
  37. int from = node;
  38. int to = edge.to;
  39. int value = edge.val;
  40. if (minDist[to] > minDist[from] + value) { // 开始松弛
  41. minDist[to] = minDist[from] + value;
  42. queue.offer(to);
  43. }
  44. }
  45. }
  46. if (minDist[end] == Integer.MAX_VALUE) {
  47. System.out.println("unconnected"); // 不能到达终点
  48. } else {
  49. System.out.println(minDist[end]); // 到达终点最短路径
  50. }
  51. scanner.close();
  52. }
  53. }
小结

邻接表存储,方便找到 上一次松弛时,更新过的节点作为出发节点所连接的边

  1. List<List<Edge>> graph = new ArrayList<>();
  2. for (int i = 0; i <= n; i++) {
  3. graph.add(new ArrayList<>());
  4. }

 使用LinkedList实现Queue,不断从中 poll 出节点node,操作 node 的 edge,将 node.to 加入到队列中

  1. Queue<Integer> queue = new LinkedList<>();
  2. queue.offer(start); // 队列里放入起点
  3. while (!queue.isEmpty()) {
  4. int node = queue.poll();
  5. for (Edge edge : graph.get(node)) {
  6. int from = node;
  7. int to = edge.to;
  8. int value = edge.val;
  9. if (minDist[to] > minDist[from] + value) { // 开始松弛
  10. minDist[to] = minDist[from] + value;
  11. queue.offer(to);
  12. }
  13. }
  14. }

95.城市间货物运输|| (Bellman_ford判断负权回路)

题目:95. 城市间货物运输 II (kamacoder.com)

思路:出现负权回路,按照之前的思路,会一直循环回路,使得成本不断减小,因此核心思路是,在Bellman_ford标准版基础上,再松弛一次,看结果是否变化

SPFA(Bellman_ford优化版),则是看节点加入队列次数是否超过n-1次

答案
  1. import java.util.*;
  2. public class Main {
  3. public static void main(String[] args) {
  4. Scanner scanner = new Scanner(System.in);
  5. int n = scanner.nextInt(); // 顶点数
  6. int m = scanner.nextInt(); // 边数
  7. List<int[]> edges = new ArrayList<>();
  8. // 将所有边保存起来
  9. for (int i = 0; i < m; i++) {
  10. int p1 = scanner.nextInt();
  11. int p2 = scanner.nextInt();
  12. int val = scanner.nextInt();
  13. edges.add(new int[]{p1, p2, val});
  14. }
  15. int start = 1; // 起点
  16. int end = n; // 终点
  17. int[] minDist = new int[n + 1];
  18. Arrays.fill(minDist, Integer.MAX_VALUE);
  19. minDist[start] = 0;
  20. boolean flag = false;
  21. // 对所有边松弛 n 次,最后一次判断负权回路
  22. for (int i = 1; i <= n; i++) {
  23. for (int[] edge : edges) {
  24. int from = edge[0];
  25. int to = edge[1];
  26. int price = edge[2];
  27. if (i < n) {
  28. if (minDist[from] != Integer.MAX_VALUE && minDist[to] > minDist[from] + price) {
  29. minDist[to] = minDist[from] + price;
  30. }
  31. } else { // 多加一次松弛判断负权回路
  32. if (minDist[from] != Integer.MAX_VALUE && minDist[to] > minDist[from] + price) {
  33. flag = true;
  34. }
  35. }
  36. }
  37. }
  38. if (flag) {
  39. System.out.println("circle");
  40. } else if (minDist[end] == Integer.MAX_VALUE) {
  41. System.out.println("unconnected");
  42. } else {
  43. System.out.println(minDist[end]);
  44. }
  45. scanner.close();
  46. }
  47. }

95.城市间货物运输|||(Bellman_ford单源有限最短路径)

题目:96. 城市间货物运输 III (kamacoder.com)

思路:关键是在于,每次松弛要基于上一次松弛的结果

答案
  1. import java.util.*;
  2. public class Main {
  3. public static void main(String[] args) {
  4. Scanner scanner = new Scanner(System.in);
  5. int n = scanner.nextInt(); // 顶点数
  6. int m = scanner.nextInt(); // 边数
  7. List<int[]> edges = new ArrayList<>();
  8. // 将所有边保存起来
  9. for (int i = 0; i < m; i++) {
  10. int p1 = scanner.nextInt();
  11. int p2 = scanner.nextInt();
  12. int val = scanner.nextInt();
  13. edges.add(new int[]{p1, p2, val});
  14. }
  15. int src = scanner.nextInt(); // 起点
  16. int dst = scanner.nextInt(); // 终点
  17. int k = scanner.nextInt(); // 最大边数
  18. int[] minDist = new int[n + 1];
  19. Arrays.fill(minDist, Integer.MAX_VALUE);
  20. minDist[src] = 0;
  21. int[] minDistCopy = new int[n + 1];
  22. // 进行 k+1 次松弛操作
  23. for (int i = 1; i <= k + 1; i++) {
  24. System.arraycopy(minDist, 0, minDistCopy, 0, minDist.length); // 获取上一次计算的结果
  25. for (int[] edge : edges) {
  26. int from = edge[0];
  27. int to = edge[1];
  28. int price = edge[2];
  29. // 注意使用 minDistCopy 来计算 minDist
  30. if (minDistCopy[from] != Integer.MAX_VALUE && minDist[to] > minDistCopy[from] + price) {
  31. minDist[to] = minDistCopy[from] + price;
  32. }
  33. }
  34. }
  35. if (minDist[dst] == Integer.MAX_VALUE) {
  36. System.out.println("unreachable"); // 不能到达终点
  37. } else {
  38. System.out.println(minDist[dst]); // 到达终点最短路径
  39. }
  40. scanner.close();
  41. }
  42. }
小结

更新用的是minDist,判断用的是minDistCopy

  1. if (minDistCopy[from] != Integer.MAX_VALUE && minDist[to] > minDistCopy[from] + price) {
  2. minDist[to] = minDistCopy[from] + price;
  3. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/804915
推荐阅读
相关标签
  

闽ICP备14008679号