【Java】求连通图中的K个连通分量权值之和的最小值Pro20210720(最小生成树(MST) / 克鲁斯卡尔 Kruskal)

本文介绍了如何使用Kruskal算法解决在S国旅行的问题,旨在找到K次旅行中,通过最少的道路访问所有城市的最低费用。文章详细阐述了算法思路,并提供了代码实现,包括未优化和优化后的版本,优化点在于只需要构建(N-K+1)条边的最小生成树。
摘要由CSDN通过智能技术生成

【Java】求连通图中的K个连通分量权值之和的最小值Pro20210720(最小生成树(MST) / 克鲁斯卡尔 Kruskal)

题目

琳恩正在计划前往S国进行K次旅行。
S国有N座城市,城市之间通过M条道路连接着。琳恩可从任意一座城市前往另一座城市,移动时,每条道路均会产生通行费,所以她需要支付费用。
琳恩计划在K次旅行期间,通过利用最少的道路来访问N座城市,而每座城市至少需要访问一次。如果按照如上的方式旅行时,存在多种方案的话,琳恩想找出其中花费最低的出行方案作为旅行计划。
琳恩可以选择K次旅行时的出发城市,但她必须要返回出发城市才能结束此次旅行。请帮助琳恩求出旅行时所需要的最低费用。

在这里插入图片描述

如上图所示,假设分成两次来旅行六座城市,第一次旅行是从城市①出发,沿着 ①→②→③→②→① 的路径旅行,这时她利用了两条道路旅行了三座城市,最终花费了8。(再次途经同一条道路时,虽然利用的道路次数不会增加,但需要再次支付通行费)。如果沿着 ①→②→③→① 的路径旅行时,虽然产生的费用会更低,花费是7,但她不会采取该路线,因为该路线需要经过更多的道路。第二次旅行是从城市⑥出发,沿着⑥→④→⑥→⑤→⑥旅行,这时她仅利用了两条道路旅行了三座城市,花费为18。按照以上计划,通过两次旅行,她只需利用四条道路便可旅行所有的六座城市,花费为26。这是琳恩想要的最佳旅行方案之一。

[限制条件]
1.城市数量N为介于3到100,000之间的整数。
2.道路数量M为介于N-1到200,000之间的整数。
3.旅行次数K为介于2到N之间的整数。
4.道路通行费C为介于1到100,000之间的整数。
5.再次途径相同道路时,仍然需要支付通行费。

[输入]
首先给出测试用例数量T,接着给出T种测试用例。每个测试用例的第一行空格区分给出城市数N、道路数M以及旅行次数K。第二行开始,通过M行,每行按照A B C(城市A,城市B,通行费C)的格式空格区分给出道路信息。

[输出]
每个测试用例输出一行。首先输出“#x”(x为测试用例编号,从1开始),加一个空格,输出K 次旅行所需要的最低费用。

[输入输出 示例]
(输入)
2
6 10 2
1 2 2
1 3 3
1 5 5
2 4 6
2 3 2
3 4 5
3 6 5
3 5 5
4 6 5
5 6 4
6 10 3
1 2 2
1 3 3
1 5 5
2 4 6
2 3 2
3 4 5
3 6 5
3 5 5
4 6 5
5 6 4

(输出)
#1 26
#2 16

思路

克鲁斯卡尔求出原图的最小生成树,然后每去掉一条边(要使用的边的权值之和最小,则需要去掉最小生成树中权值最大的边),就会被多分出来一个连通分量。

代码(调整前):

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
   
	static int T, N, M, K, MST_EDGE_COUNT;
	static long ASW;
	static ArrayList<Edge> DATA;
	static ArrayList<Long> MST;
	static int[] UF;

	static class Edge {
   
		int from, to, cost;

		public Edge(int s, int e, int c) {
   
			this.from = s;
			this.to = e;
			this.cost = c;
		}
	}

	public static void main(String[] args) throws IOException {
   
		System.setIn(new FileInputStream("D:\\SW\\TestCase\\sample_input_20210720.txt"));
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine());

		int T = Integer.parseInt(st.nextToken()); // Case数量

		for (int t = 1; t <= T; t++) {
   
			st = new StringTokenizer(br.readLine());
			N = Integer.parseInt(st.nextToken())
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值