路径 Dijkstra 蓝桥杯 JAVA

文章介绍了Dijkstra算法的基本思想和应用,提供了一个模板代码示例,并详细解释了算法在解决特定图结构(节点差值大于21无边,小于等于21的节点间有边,边长为两节点最小公倍数)中找到节点1到节点2021的最短路径问题。通过Java代码展示了如何计算给定图的最短路径。
摘要由CSDN通过智能技术生成

题目描述:

小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。 小蓝的图由2021 个结点组成,依次编号1 至2021。
对于两个不同的结点a, b,如果a 和b 的差的绝对值大于21,则两个结点之间没有边相连; 如果a 和b
的差的绝对值小于等于21,则两个点之间有一条长度为a 和b 的最小公倍数的无向边相连。 例如:结点1 和结点23 之间没有边相连;结点3
和结点24 之间有一条无向边,长度为24; 结点15 和结点25 之间有一条无向边,长度为75。 请计算,结点1 和结点2021
之间的最短路径长度是多少。

提示:建议使用计算机编程解决问题

答案:10266837


Dijkstra 算法 (朴素版):

迪杰斯特拉(dijkstra)算法是单源最短路径问题的求解方法。单源最短路径就在给出一个固定网络,指定一个原点s,一个目标点e,求这两个点之间的最短路径。

迪杰斯特拉算法是,基于贪心算法,目标是求最短路径,既然整个路径是最短得,那么分支也应该是最短的,所以按我的理解,迪杰斯特拉算法,就是在找最短子路径,然后迭代下去,这样每个子路径是最优的,那么整个路径就是最优的

需要的数组有一维数组distdist[i]记录的是i点到起点的距离,edges二维数组edges[i][j]表示点i到点j的距离。最后是visit一维数组visit[i]表示每个点是否被遍历过

如果对这个算法一点不了解的话可以看这个大哥讲的是真不错:迪杰斯特拉算法详讲

这里直接给出dijkstra的模板代码:

import java.util.*;

public class Main {
    public static int edges[][];//存放所有边edges[i][j]代表从i ~ j 的距离
    public static int dist[];//存入起点到所有点的距离。
    public static boolean visit[];//标记当前点是否被遍历过
    public static int INF = 0x3f3f3f3f;
	public static void main(String[] args) {
		
	}
    public static int dijkstra(int n) {//传入终点
    	for(int i = 1; i <= n; i ++) {
    		int index = -1;//未被访问的点,到原点最短距离的点。
    		dist[1] = 0;//原点到原点的距离为0
    		for(int j = 1; j <= n; j ++) {//找最小点
    			if(!visit[j] &&(index == -1 || dist[j] < dist[index])) {
    				index = j;
    			}
    		}
    		visit[index] = true;//标记用过
    		for(int j = 1; j <= n; j ++) {//更新
    			if(dist[index] + edges[index][j] < dist[j]) {
    				dist[j] = dist[index] + edges[index][j];
    			}
    		}
    	}
    	if(dist[n] == INF) return - 1;
    	return dist[n];
    }
}

这里要注意的是迪杰斯特拉算法,边的权值不能为负,真是一条边为负都不行:

以下图为例:
在这里插入图片描述
代码:

import java.util.*;

public class Main {
    public static int edges[][];//存放所有边edges[i][j]代表从i ~ j 的距离
    public static int dist[];//存入起点到所有点的距离。
    public static boolean visit[];//标记当前点是否被遍历过
    public static int INF = 0x3f3f3f3f;
	public static void main(String[] args) {
		 edges = new int[5][5];
		 dist = new int[5];
		 visit = new boolean[5];
		 Arrays.fill(dist, INF);
		 
		 for(int i = 1; i <= 4; i ++)
			 for(int j = 1; j <= 4; j ++) edges[i][j] = INF;
		 
		 edges[1][1] = 0;
		 edges[2][2] = 0;
		 edges[3][3] = 0;
		 edges[4][4] = 0;
		 
		 edges[1][2] = 1; 
		 edges[1][3] = 2;
		 edges[1][4] = 8;
		 edges[2][4] = 4;
		 edges[3][4] = 5;
		 edges[3][2] = -2;
		 
		 
		 System.out.println(dijkstra(4));
		 
		// for(int i = 1; i <= 4; i ++) System.out.print(dist[i] + " ");
		 
	}
    public static int dijkstra(int n) {//传入终点
    	for(int i = 1; i <= n; i ++) {
    		int index = -1;//未被访问的点,到原点最短距离的点。
    		dist[1] = 0;//原点到原点的距离为0
    		for(int j = 1; j <= n; j ++) {//找最小点
    			if(!visit[j] &&(index == -1 || dist[j] < dist[index])) {
    				index = j;
    			}
    		}
    		visit[index] = true;
    		for(int j = 1; j <= n; j ++) {
    			if(dist[index] + edges[index][j] < dist[j]) {
    				dist[j] = dist[index] + edges[index][j];
    			}
    		}
    	}
    	if(dist[n] == INF) return - 1;
    	return dist[n];
    }
}

输出:
在这里插入图片描述

正确答案是 4,这是迪杰斯特拉算法特性导致的,最开始点2到原点距离最短,会选择点2进行更新,但不会选择点3
后续即使用点3更新了点2,由于点2已经被使用过了,所以无法再使用了。
本算法和floyd算法本质不同是floyd算法记录了任意两点的最优距离,本算法是记录任意一点到起点的最优距离。(Floyd算法可以允许路径有负权,但回路不能为负。)


用Dijkstra解决本题:

了解dijkstra后,代码就非常好写了,这里直接给出本题代码。

代码:

import java.util.Arrays;

public class text {
	 public static int edges[][];//存放所有边edges[i][j]代表从i ~ j 的距离
	 public static int dist[];//存入起点到所有点的距离。
	 public static boolean visit[];//标记当前点是否被遍历过
	 public static int INF = 0x3f3f3f3f;
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		  int n = 2022;
		  edges = new int[n][n];
		  dist = new int[n];
		  visit = new boolean[n];
		  for(int i = 1; i <= 2021; i ++)
			  for(int j = 1; j <= 2021; j ++) {
				  if(Math.abs(i - j) <= 21) {
					  edges[i][j] = getgb(i, j);
				  }
				  else
					  edges[i][j] = INF;
			  }
		  Arrays.fill(dist, INF);
		  System.out.print(dijkstra(2021));
	}
	public static int getgb(int x, int y) {//最小公因数
		int maxgy = 1;
		int minone = Math.min(x, y);
		for(int i = 1; i <= minone; i ++)
			if(x % i == 0 && y % i == 0)
				maxgy = i;
		return x * y / maxgy;
	}
    public static int dijkstra(int n) {
    	for(int i = 1; i <= n; i ++) {
    		int index = -1;
    	    dist[1] = 0;//初始化
    	    for(int j = 1; j <= n; j ++) {//找距离原点最近的点
    	    	if(!visit[j] && (index == -1 || dist[j] < dist[index])) {
    	    		index = j;
    	    	}
    	    } 
    	    visit[index] = true;//标记用过
    	    for(int j = 1; j <= n; j ++) {//更新
    	       if(dist[index] + edges[index][j] < dist[j]) {	
    	    	   dist[j] = dist[index] + edges[index][j];
    	       }
    	    }
    	}
    	if(dist[n] == INF) return - 1;
    	return dist[n];
    }
}

Dijkstra算法是一种用于解决带权重图中单源最短路径问题的贪心算法。这里所谓的单源最短路径,就是指在图中找到一个顶点,使得从该顶点出发到其他所有顶点的路径长度最短。 Dijkstra算法的基本思路是:对于给定的起点,首先将其标记为已访问,并设置起点到起点自身的距离为0。然后,遍历该起点能够到达的所有节点,并计算它们到起点的距离。选择距离最短的节点,标记为已访问,并将该节点作为中间节点,继续遍历其能够到达的所有节点,并更新它们到起点的距离。重复这个过程,直到所有节点都被标记为已访问。 以下是Dijkstra算法Java实现: ``` import java.util.*; public class DijkstraAlgorithm { private static int[][] graph; private static int[] dist; private static boolean[] visited; public static void main(String[] args) { Scanner scanner = new Scanner(System.in); // 输入图的大小和边的数量 int n = scanner.nextInt(); int m = scanner.nextInt(); // 初始化图 graph = new int[n][n]; for (int i = 0; i < m; i++) { int u = scanner.nextInt(); int v = scanner.nextInt(); int w = scanner.nextInt(); graph[u - 1][v - 1] = w; graph[v - 1][u - 1] = w; } // 输入起点和终点 int start = scanner.nextInt() - 1; int end = scanner.nextInt() - 1; // 初始化距离数组和访问数组 dist = new int[n]; visited = new boolean[n]; Arrays.fill(dist, Integer.MAX_VALUE); dist[start] = 0; // 开始遍历 for (int i = 0; i < n; i++) { // 找到当前未访问节点中距离起点最近的节点 int minDist = Integer.MAX_VALUE; int u = -1; for (int j = 0; j < n; j++) { if (!visited[j] && dist[j] < minDist) { minDist = dist[j]; u = j; } } if (u == -1) break; // 标记当前节点为已访问 visited[u] = true; // 更新与当前节点相邻的节点的距离 for (int v = 0; v < n; v++) { if (graph[u][v] > 0 && !visited[v] && dist[u] + graph[u][v] < dist[v]) { dist[v] = dist[u] + graph[u][v]; } } } // 输出结果 System.out.println(dist[end]); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值