单源最短路(优先队列优化的dijkstra算法):PIPI的飞行路线

- 日理万妓 2023-10-06 20:03 137阅读 0赞

单源最短路(优先队列优化的dijkstra算法):PIPI的飞行路线

文章目录

  • 单源最短路(优先队列优化的dijkstra算法):PIPI的飞行路线
      • dijkstra算法
      • 问题:
      • 思路:
      • 代码:

dijkstra算法

  dijkstra算法用于解决不含负权边单源最短路径问题。首先要知道两个点之间的最短路径有以下两种情况:
  (1)连接两点的边本身就是最短的。
  (2)两点之间通过一些中间节点相连得到最短距离。
  dijkstra算法用dis数组保存当前源点s距离其他点的最短距离。初始时,dis数组除了源点离自己的距离dis[s]为0,其他全为INF。dijkstra算法利用了贪心的思想,每次从dis数组中筛出最短距离还未确定且最小的dis[x],此时源点到顶点x的最短距离就确定了(不可能更小了),然后从x出发更新其相邻节点的最短距离。dijkstra算法重复上述过程直到所有顶点的最短距离都确定。
  朴素的dijkstra算法的时间复杂度为O(n^2),筛出数组中最短距离需要O(n),更新相邻结点的最短距离需要O(n)。我们可以用优先队列对筛选最短距离的过程进行优化。
  使用优先队列优化的dijkstra算法与使用优先队列的BFS算法如出一辙。同样的,我们用dis数组保存当前源点s距离其他点的最短距离。初始时,dis数组除了源点离自己的距离dis[s]为0,其他全为INF。之后,将源点s压入优先队列,开始BFS过程。对于每次出队的结点u,我们对其连接的下个结点v进行判断,如果dis[v]>dis[u]+(u,v)边权,则更新dis [v] (即令dis[v]=dis[u]+(u,v)边权),并将v结点入队。重复上述过程直到队列中再无任何结点,最终得到的dis数组即为源点s到各点的最短路径。优先队列将朴素方法中筛选最短距离的过程进行优化,将O(n)优化到了O(logn)

  在朴素的dijkstra算法中,我们会使用一个标记数组来表示源点到某结点的最短距离是否已经确定,若确定了,则可以不进行最短路径的更新了。而在优先队列的优化下,可以不使用标记数组了。我们每次向队列中加入结点都会附带源点到该节点暂时得到的最短路径,若我们从队列中取出的头结点中的该路径值比dis[]数组中的距离大,则证明该状态可以被舍弃,已经有更优的从源点走到该节点的路径,我们可以使用该判断过滤掉无用的状态,通过不断更新最短距离,最终dis[]数组也会维持到所有最短距离都确定的状态,因此可以不使用标记数组了。

  优先队列版的dijkstra算法模板如下,时间复杂度为O((n+m)*logn):

  1. static final int INF = 100000009;
  2. static Queue<Node> q = new PriorityQueue<>();
  3. // 邻接表
  4. static ArrayList<Node>[] adj = new ArrayList[10002];
  5. // 距离数组dis[]
  6. static int[] dis = new int[10002];
  7. static void dijstra(int s) {
  8. int i, index, dis, indexOther, weight;
  9. // dis数组初始化
  10. for (i = 1;i <= n;i++) {
  11. dis[i] = INF;
  12. }
  13. // 源点到自己的距离为0
  14. dis[s] = 0;
  15. q.add(new Node(s, 0));
  16. Node node;
  17. while (!q.isEmpty()) {
  18. node = q.poll();
  19. index = node.index;
  20. dis = node.distance;
  21. // 若该节点保存的distance大于dis数组中保存的从s到该节点的最短路径,则直接舍弃该状态
  22. if (dis > distance[index]) {
  23. continue;
  24. }
  25. for (i = 0;i < adj[index].size();i++) {
  26. indexOther = adj[index].get(i).index;
  27. weight = adj[index].get(i).distance;
  28. // 更新最短路径
  29. if (distance[indexOther] > dis + weight) {
  30. distance[indexOther] = dis + weight;
  31. q.add(new Node(indexOther, distance[indexOther]));
  32. }
  33. }
  34. }
  35. }
  36. class Node implements Comparable<Node> {
  37. public int index;
  38. public int distance;
  39. public Node(int index, int distance) {
  40. this.index = index;
  41. this.distance = distance;
  42. }
  43. @Override
  44. public int compareTo(Node o) {
  45. return this.distance - o.distance;
  46. }
  47. }

问题:

在这里插入图片描述
在这里插入图片描述

思路:

  此题在单源最短路的基础上增加了一个免费路线情况,麻烦点也在于此,我们可以从所有免费路线中选取一条
  所有可选的免费路线并不多,我们会想到枚举所有情况,寻找最短距离,那么如何比较选取一条免费路线比另一条免费路线好呢?若我们选取(u,v)这条免费路线,那么我们从源点s到终点n只有两种情况:

  • s——>u——>v——>n
  • s——>v——>u——>n

  考虑dijkstra算法最后得到的dis[]数组,我们可以知道s——>u和s——>v的最短距离,而从u——>v和从v——>u的距离为0,最后只剩下从v——>n和u——>n的最短距离,注意到该题中的图为无向图,因此v——>n和n——>v的最短距离是相等的。因此我们再将n设为源点,进行一次dijkstra算法,得到n到其他点的最短距离。这样我们就能比较选择不同免费线路,哪个最短了。若用dis1[]表示源点为s的距离数组,dis2[]表示源点为n的距离数组,则选择(u,v)免费路线,从s到n的最短距离为:
  min(dis1[u]+dis2[v],dis1[v]+dis2[u])
  枚举所有免费路线的情况,获取使用免费机票时的最短路径,再与不选取免费路线的最短路径比较,较小者即为答案

代码:

  1. import java.util.*;
  2. public class Main {
  3. static final int INF = 100000009;
  4. static Queue<Node> q = new PriorityQueue<>();
  5. static ArrayList<Node>[] adj = new ArrayList[10002];
  6. static int[] dis1 = new int[10002];
  7. static int[] disN = new int[10002];
  8. public static void main(String[] args) {
  9. int i, n, m, u, v, c, k, ans;
  10. Scanner scanner = new Scanner(System.in);
  11. n = scanner.nextInt();
  12. m = scanner.nextInt();
  13. for (i = 0;i <= n;i++) {
  14. dis1[i] = INF;
  15. disN[i] = INF;
  16. adj[i] = new ArrayList<>();
  17. }
  18. dis1[1] = 0;
  19. disN[n] = 0;
  20. for (i = 0;i < m;i++) {
  21. u = scanner.nextInt();
  22. v = scanner.nextInt();
  23. c = scanner.nextInt();
  24. adj[u].add(new Node(v, c));
  25. adj[v].add(new Node(u, c));
  26. }
  27. q.add(new Node(1, 0));
  28. dijstra(dis1);
  29. q.add(new Node(n, 0));
  30. dijstra(disN);
  31. ans = dis1[n];
  32. k = scanner.nextInt();
  33. for (i = 0;i < k;i++) {
  34. u = scanner.nextInt();
  35. v = scanner.nextInt();
  36. ans = Math.min(ans, Math.min(dis1[u] + disN[v], disN[u] + dis1[v]));
  37. }
  38. if (ans == INF) {
  39. System.out.println("No way!");
  40. } else {
  41. System.out.println(ans);
  42. }
  43. }
  44. static void dijstra(int[] distance) {
  45. Node node;
  46. int i, index, dis, indexOther, weight;
  47. while (!q.isEmpty()) {
  48. node = q.poll();
  49. index = node.index;
  50. dis = node.distance;
  51. if (dis > distance[index]) {
  52. continue;
  53. }
  54. for (i = 0;i < adj[index].size();i++) {
  55. indexOther = adj[index].get(i).index;
  56. weight = adj[index].get(i).distance;
  57. if (distance[indexOther] > dis + weight) {
  58. distance[indexOther] = dis + weight;
  59. q.add(new Node(indexOther, distance[indexOther]));
  60. }
  61. }
  62. }
  63. }
  64. }
  65. class Node implements Comparable<Node> {
  66. public int index;
  67. public int distance;
  68. public Node(int index, int distance) {
  69. this.index = index;
  70. this.distance = distance;
  71. }
  72. @Override
  73. public int compareTo(Node o) {
  74. return this.distance - o.distance;
  75. }
  76. }

发表评论

表情:
评论列表 (有 0 条评论,137人围观)

还没有评论,来说两句吧...

相关阅读

    相关 短路dijkstra算法

    一直想着把这个板子存一下,但老是忘了,结果每次还得自己手打 dijkstra最短路算法有两种方法 第一种n^2的时间,用一个数组维护起点到所有点最短距离,不断的用最新点连进来