WUSTOJ 1319: 球(Java)并查集

题目链接:1319: 球
参考:wustoj 1319 球—wust_tanyao并查集
并查集系列:WUSTOJ 1346: DARK SOULS(Java)并查集

Description

Icy is a lonely boy. He was so boring these days. So he started to play a game himself. First, he took a heap of balls with many different colors. ( One ball with only one color ) Then he repeated to pick two balls and put them back. Each time the two balls he chose is of the same color. After M times, Icy was too boring to fall asleep.
Now, a boring people wanna ask you a very easy question: how many different colors are there in the original heap at most ?

Input

The first line contains one integer T indicating the number of test cases.
For each test case, the first line contains two integer numbers N and M (0 <= N,M<= 10000) that represents the total number of balls and the number of times Icy took balls.Balls are numbered from 1 to N.Each of the following M lines contains two integer numbers A and B (1 <= A, B <= N). It means that Icy picked ball A and ball B.

一共有N个球,每个球只有一种颜色,每次取出颜色相同的两个球,然后放回去,取M次,问最多有多少种颜色?

Output

For each test case, output an integer that satisfies the problem description.

Sample Input

2
3 3
1 2
2 3
1 3
3 1
1 2

Sample Output

1
2

分析💬

第一次正式接触并查集这个概念。概念不是很复杂。就是最初有一个集合,每个元素构成一个子集,后来由于某种关系使得某些元素之间联系在一起合并为一个子集。我的理解大致就是这个意思了。

对于此题而言,将相同颜色的球组成一个子集,然后求子集的个数,即为颜色最多的种数。

这里采用并查集森林短路径压缩来实现。

  • 最初,每个球是一棵树,父结点是自己。最初的父结点也是根结点。用int型一维数组father[N + 1]表示。总的颜色数量ans和球的个数相同。
  • 如果有2个球同时取出,说明颜色一样。将A球所在的树合并到B球所在的树(将A树的根结点的父结点修改为B树的根结点即可,意思就是A树现在变成了B树根结点的儿子)。当然合并的时候,少了一棵树,因此总的颜色数量ans要减1。
  • 不断重复上述过程即可构造出最终的森林。此时ans就是最终结果。
  • 由于用到了短路径压缩的方法,因此在查找根结点的过程中要将这棵树的非根结点父结点全部修改为根结点,一句代码搞定:father[node] = findRoot(father[node]),效果是减小树的深度。使用这个方法后,所有的树的深度不会超过3,这为以后查找节约时间。

代码

/**
 * Time 1192ms
 * @author wowpH
 * @version 2.2
 * @date 2019年6月22日下午8:30:16
 * Environment:	Windows 10
 * IDE Version:	Eclipse 2019-3
 * JDK Version:	JDK1.8.0_112
 */

import java.io.InputStreamReader;
import java.util.Scanner;

public class Main {
	private Scanner sc = new Scanner(new InputStreamReader(System.in));
	private int[] father;	// 保存父结点,下标从1开始
	private int ans;		// 最终颜色种类数量
	private int T, N, M;	// 测试组数,小球个数,取球次数

	public Main() {
		int A, B;// 取出的小球编号
		T = sc.nextInt();
		while ((T--) > 0) {
			init();// 初始化
			for (int i = 0; i < M; ++i) {
				A = sc.nextInt();
				B = sc.nextInt();
				merge(A, B);// 合并集合
			}
			System.out.println(ans);// 输出结果
		}
		sc.close();
	}

	private void init() {
		N = sc.nextInt();// 球个数
		M = sc.nextInt();// 取球次数
		ans = N;		// 最多N种颜色
		father = new int[N + 1];
		for (int i = 1; i <= N; ++i) {
			father[i] = i;// 标记父结点为自己
		}
	}

	private void merge(int A, int B) {// 合并集合
		int aRoot = findRoot(A);// 查找A的根结点
		int bRoot = findRoot(B);// 查找B的根结点
		if (aRoot != bRoot) {	// 两个的根结点不同
			ans = ans - 1;		// 现在要变成相同,因此颜色种类要减1
			father[aRoot] = bRoot;// A结点的根(父)结点的父结点指向B的根结点
		}
	}

	private int findRoot(int node) {// 查找node的父结点
		if (father[node] == node) {// 如果node的父结点是自己
			return node;// 直接返回node
		}
		father[node] = findRoot(father[node]);// 更新node的父结点
		return father[node];// 返回node的父结点(此时父结点就是根结点)
	}

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

版权声明

  1. 转载、参考、引用必须在首页添加如下文字:
    [WUSTOJ 1319: 球(Java)并查集——wowpH](https://blog.csdn.net/pfdvnah/article/details/93356519)
  2. 代码原创,公开引用不能删除首行注释(作者,版本号,时间等信息);
  3. 如果有疑问欢迎评论区留言,尽量解答;
  4. 如果有错误,还望大侠评论区指正。

posted @ 2019-06-23 11:04  wowpH  阅读(166)  评论(0编辑  收藏  举报