蓝桥杯2021省赛填空题最后一题:图的遍历和最大公因数(小蓝的图由 2021 个结点组成,依次编号 1 至 2021。)

2023-10-30

蓝桥杯2021省赛填空题最后一题:
/**

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

*/

其中无非就是考察两个知识点: 一个是 最小公倍数一个是图的单源路径

这里使用迪杰斯特拉给出邻接矩阵和邻接表的两种形式:


public class 迪杰斯特拉邻接矩阵 {

	private static int[][] vetex;// 邻接矩阵
	private static final int MAX = Integer.MAX_VALUE;
	//这个开始的sn是说从哪个结点开始 dist[i]数组存储的是从sn出发到i结点最短的距离
	public static void test(int sn, int dist[]) { 
//		 初始化访问标记数组
		boolean[] flag = new boolean[vetex.length];
		flag[sn] = true;
//		为 0 就是不通!
		for (int i = 1; i < vetex.length; i++) {
			dist[i] = vetex[sn][i]; // 初始化dist数组
		}
//		遍历前 找到最小的距离 并且置位
//		遍历 length -1 次就可以了
		int num = 1;
//		截止目前 已经有一个设置为访问过了,并且标记为 true了
		for (int i = 2; i < flag.length; i++) {

//			再找当前的最小的 所以初始值为 max
			int min = MAX;
//			这个地方从1开始是因为我数组存的就是 从1 开始到 2021 (说的是索引)
			for (int j = 1; j < dist.length; j++) {
				if (!flag[j] && dist[j] != 0 && min > dist[j]) {
					min = dist[j];
					num = j;
				}
			}

			flag[num] = true;

//			更新 dist数组 
			for (int j = 1; j < dist.length && min != MAX; j++) {
				int tmp = dist[num] + vetex[num][j];
//				开始没写出来就是毁到了这个条件上,老是写错!
				if (!flag[j] && vetex[num][j] != 0) {

					if (dist[j] == 0 || dist[j] > tmp) {
						dist[j] = tmp;
					}

				}
			}
		}
		System.out.println("final:   " + dist[2021]);
	}
// 求最大公因数比较简单的一个方法  最小公倍数 = a*b / 最大公因数
	public static int gbc(int a, int b) {
		return b == 0 ? a : gbc(b, a % b);
	}

	public static void main(String[] args) {
		test2();
	}

	public static void test2() {
		vetex = new int[2022][2022];
		for (int i = 1; i < 2022; i++) {
			int min = Math.max(i - 21, 1);
			for (int j = min; j <= i; j++) {
				int div = gbc(i, j);
				int lcm = j * i / div;
				vetex[j][i] = lcm;
				vetex[i][j] = lcm;
			}
		}
		test(1, new int[2022]);
	}
}

邻接表:

public class 迪杰斯特拉Queue {
	static class Edge {
		int curr, length;
		// curr 为将要到达的点的编号,length 到达点的为路径长度
		Edge(int _curr, int _length) {
			curr = _curr;
			length = _length;
		}
	}

	static List<Edge>[] graph;
	static final int INF = 0x3f3f3f3f; //最大的int值

//	利用PriorityQueue 就是 不用自己进行排序了,他会自动根据我们的设置排序
	private static int dijkstra(int st, int ed) {
		// 新建小根堆
		PriorityQueue<Edge> pq = new PriorityQueue<>((a, b) -> Integer.compare(a.length, b.length));
		boolean[] vis = new boolean[2050];
		int[] dist = new int[2050];
		Arrays.fill(dist, INF);

		dist[st] = 0;
		// curr 为将要到达的点的编号,length 到达点的为路径长度
		pq.offer(new Edge(st, dist[st]));

		while (!pq.isEmpty()) {
			int curr = pq.poll().curr;
			if (vis[curr]) {
				continue;
			}
			vis[curr] = true;
			// 松弛操作 也就是 进行了 重新距离的更新 跟用邻接矩阵做的动作是一样的
			for (Edge next : graph[curr]) {
				int x = next.curr, len = next.length;
				if (dist[x] > dist[curr] + len) {
					dist[x] = dist[curr] + len;
					pq.offer(new Edge(x, dist[x])); //只是将各个选择走的,路径比较小的 加入 pq,(通过dist进行的比较)
				}
			}
		}

		return dist[ed];
	}

	private static int gcd(int a, int b) {
		return b == 0 ? a : gcd(b, a % b);
	}

	public static void main(String[] args) {
		graph = new List[2050];

		// 构建邻接表,每个结点都被放完全了 比如 1中有 21,21中也有1
		for (int i = 1; i <= 2021; i++) {
//			这也就是能让每个j 的开始位置为 距离其最远的 那个点
			int st = Math.max(i - 21, 1);
			for (int j = st; j <= i; j++) {
				int div = gcd(j, i);
				int lcm = i * j / div;
				if (graph[i] == null) {
					graph[i] = new ArrayList<>();
				}
				if (graph[j] == null) {
					graph[j] = new ArrayList<>();
				}
				graph[i].add(new Edge(j, lcm));
				graph[j].add(new Edge(i, lcm));
			}
		}
		System.out.println(dijkstra(1, 2021)); // 10266837
	}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

蓝桥杯2021省赛填空题最后一题:图的遍历和最大公因数(小蓝的图由 2021 个结点组成,依次编号 1 至 2021。) 的相关文章

  • SQLite基本操作

    SQLite SQLite是一个软件库 实现了自给自足的 无服务器的 零配置的 事务性的 SQL 数据库引擎 SQLite 源代码不受版权限制 SQLite 直接访问其存储文件 SQLite 是非常小的 是轻量级的 完全配置时小于 400K

随机推荐