当前位置:   article > 正文

蓝桥杯最短路(java实现)

蓝桥杯最短路(java实现)

题目

资源限制

内存限制:256.0MB Java时间限制:3.0s

问题描述

给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环)。请你计算从1号点到其他点的最短路(顶点从1到n编号)。

输入格式

第一行两个整数n, m。接下来的m行,每行有三个整数u, v, l,表示u到v有一条长度为l的边。

输出格式

共n-1行,第i行表示1号点到i+1号点的最短路。

样例输入

3 3

1 2 -1

2 3 -1

3 1 2

样例输出

-1

-2

数据规模与约定

对于10%的数据,n = 2,m = 2。

对于30%的数据,n <= 5,m <= 10。

对于100%的数据,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保证从任意顶点都能到达其他所有顶点。


代码

Ⅰ.Dijkstra算法

  1. import java.util.Scanner;
  2. public class Main {
  3. //不能设置为Integer.MAX_VALUE,否则Integer.MAX_VALUE相加会溢出导致出现负权
  4. public static int INF = 0x3f3f3f3f;
  5. public static void main(String[] args) {
  6. Scanner in = new Scanner(System.in);
  7. int n = in.nextInt(); //顶点数
  8. int m = in.nextInt(); //边数
  9. int[][] len = new int[n+1][n+1];
  10. //初始化邻接矩阵
  11. for (int i = 1; i < n+1; i++) {
  12. for (int j = 1; j < n+1; j++) {
  13. len[i][j] = INF;
  14. }
  15. }
  16. for (int i = 0; i < m; i++) {
  17. int u = in.nextInt();
  18. int v = in.nextInt();
  19. int l = in.nextInt();
  20. len[u][v] = l;
  21. }
  22. int source = 1; //源点为1
  23. dijstra(len, 1);//调用dijstra算法计算最短路径
  24. }
  25. public static void dijstra(int[][] len, int source) {
  26. //最短路径长度
  27. int[] shortest = new int[len.length];
  28. //判断该点的最短路径是否求出
  29. int[] visited = new int[len.length];
  30. //存储输出路径
  31. String[] path = new String[len.length];
  32. //初始化出发节点
  33. shortest[source] = 0; //设置出发结点的访问距离为0
  34. visited[source] = 1; //设置出发节点被访问过
  35. for (int i = 2; i < len.length; i++) {
  36. int min = INF;
  37. int index = -1;
  38. for (int j = 2; j < len.length; j++) {
  39. //已经求出最短路径的节点visit[j]==1排除在外无需判断
  40. if (visited[j] == 0 && len[source][j] < min) {
  41. min = len[source][j];
  42. index = j;
  43. }
  44. }
  45. //更新最短路径
  46. shortest[index] = min;
  47. visited[index] = 1;
  48. //更新从index跳到其它节点的较短路径
  49. for (int m = 1; m < len.length; m++) {
  50. if (visited[m] == 0 && len[source][index] + len[index][m] < len[source][m]) {
  51. len[source][m] = len[source][index] + len[index][m];
  52. }
  53. }
  54. }
  55. //打印最短路径
  56. for (int i = 2; i < len.length; i++) {
  57. System.out.println(shortest[i]);
  58. }
  59. }
  60. }
  1. 可以看到,使用Dijkstra算法不能通过所有数据。本题也不推荐使用Dijkstra算法来解决。因为题目提到有向图的部分边可能为负权,而Dijkstra算法并不能很好的解决有负权的图。

  1. 注意到代码中有public static int INF = 0x3f3f3f3f; 我们经常需要设置一个常量用来代表"无穷大"。比如对于int类型的数,有的人会采用INT_MAX,即0x7fffffff作为无穷大。但是以INT_MAX为无穷大常常面临一个问题,即加一个其他的数会溢出。所以我们常采用0x3f3f3f3f来作为无穷大。

使用INF=0x3f3f3f3f的好处:
1. 0x3f3f3f3f的十进制为1061109567,和INT_MAX一个数量级,即10^9数量级,而一般场合下的数据都是小于10^9的。
2. 0x3f3f3f3f * 2 = 2122219134, 无穷大相加依然不会溢出
3. 可以使用memset(array, 0x3f, sizeof(array))来为数组设初值为0x3f3f3f3f, 因为这个数的每个字节都是0x3fmemset是按字节来memset的,int有四个字节,就会把每个字节都变成0x3f。 一般要么memset成0,要么memset成-1,因为int 型 0每个字节都是0,-1每个字节都是-1.

Ⅱ. Bellman-ford算法

  1. import java.util.Arrays;
  2. import java.util.Scanner;
  3. public class Main {
  4. static int INF = 0x3f3f3f3f;
  5. public static void main(String[] args) {
  6. Scanner in = new Scanner(System.in);
  7. int n = in.nextInt(); //n个顶点
  8. int m = in.nextInt(); //m条边
  9. //使用邻接矩阵来存储数据
  10. int[][] len = new int[n+1][n+1];
  11. //初始化邻接矩阵
  12. for (int i = 1; i < n+1; i++) {
  13. for (int j = 1; j < n+1; j++) {
  14. len[i][j] = i == j ? 0 : INF;
  15. }
  16. }
  17. for (int i = 0; i < m; i++) {
  18. int u = in.nextInt();
  19. int v = in.nextInt();
  20. int l = in.nextInt();
  21. len[u][v] = l;
  22. }
  23. int source = 1; //源点为1
  24. getShortPaths(len,source,n); //调用函数求出最短距离
  25. }
  26. //返回第1个顶点到其它所有顶点之间的最短距离
  27. public static void getShortPaths(int[][] len, int source, int n) {
  28. int[] dist = new int[n+1]; //数组dist,存储最短距离
  29. Arrays.fill(dist, INF); //初始化数组dist[]
  30. dist[source] = 0; //令出发的dist[]为0,即dist[1]为0
  31. for (int limit = 0; limit < n-1; limit++) {
  32. int[] clone = dist.clone(); //对dist[]进行备份
  33. for (int i = 1; i < n+1; i++) {
  34. for (int j = 1; j < n+1; j++) {
  35. //松弛操作使用了从 i 到 j 的当前最短距离来更新 dist[j]
  36. //注意在迭代时使用备份clone[i]来进行更新操作
  37. dist[j] = Math.min(dist[j], clone[i] + len[i][j]);
  38. }
  39. }
  40. }
  41. //打印最短路径
  42. for (int i = 2; i < n+1; i++) {
  43. System.out.println(dist[i]);
  44. }
  45. }
  46. }
  1. Dijkstra算法不能解决带有负权边的问题,而Bellman-ford算法可以解决带有负权边的问题,是求解带负权边的单源最短路问题的经典算法。但本题用Bellman-ford算法也无法通过是因为其通过遍历所有的边来找到最短路径,对于数据量大的测试而言容易造成内存超限和运行超时。

  1. 需要注意的是,在遍历所有的“点对/边”进行松弛操作前,需要先对 dist 进行备份,否则会出现「本次松弛操作所使用到的边,也是在同一次迭代所更新的」,从而不满足边数限制的要求。举个

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