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

蓝桥杯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
	}
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杰夫·王盖茨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值