问题描述
w星球的一个种植园,被分成 m * n 个小格子(东西方向m行,南北方向n列)。每个格子里种了一株合根植物。
这种植物有个特点,它的根可能会沿着南北或东西方向伸展,从而与另一个格子的植物合成为一体。
如果我们告诉你哪些小格子间出现了连根现象,你能说出这个园中一共有多少株合根植物吗?
输入格式
第一行,两个整数m,n,用空格分开,表示格子的行数、列数(1<m,n<1000)。
接下来一行,一个整数k,表示下面还有k行数据(0<k<100000)
接下来k行,第行两个整数a,b,表示编号为a的小格子和编号为b的小格子合根了。
格子的编号一行一行,从上到下,从左到右编号。
比如:5 * 4 的小格子,编号:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
17 18 19 20
样例输入
5 4
16
2 3
1 5
5 9
4 8
7 8
9 10
10 11
11 12
10 14
12 16
14 18
17 18
15 19
19 20
9 13
13 17
样例输出
5
样例说明
其合根情况参考下图
解题思路:
看到这题,我第一时间想到的是四连通图的解决方法,但是后来尝试了一下不行,因为这个数据量太大了,用深搜的话会超时的,而且将这些数据转换为图上的块也很麻烦。
其实这题用并查集更容易解决,建立一个大小为n*m+1的数组(因为数据从1开始计算),将数组初始化为-1;用-1表示这个节点没有父节点,即这个节点作为一个集合的代表。
当所有的节点合并完成后,遍历数组,统计-1的个数,即集合的个数(没有参数合根的点会单独作为一个集合)。-1的个数就是想要的结果。
关于并查集的使用可以看这篇文章:并查集
解题代码:
import java.util.Arrays;
import java.util.Scanner;
public class 合根植物 {
static int n; //表示列
static int m; //表示行
static int k; //表示给出数据的行数
static int[] arr; //用于并查集
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
m = scanner.nextInt();
n = scanner.nextInt();
k = scanner.nextInt();
arr = new int[n*m+1]; //从1开始使用,0位不用
Arrays.fill(arr, -1); //用-1表示根节点,
int a, b;
for(int i=0; i<k; i++){
a = scanner.nextInt();
b = scanner.nextInt();
if(union(a,b)){
continue;
}
}
int count = 0; //用于计算有多少种植物
for(int i=1; i<arr.length; i++){
if(arr[i]==-1){
count++;
}
}
System.out.println(count);
}
//将两个集合合并
private static boolean union(int x, int y) {
int xroot = findRoot(x);
int yroot = findRoot(y);
if(xroot==yroot){
return true;
}
arr[xroot] = yroot; //将x子树的根节点指向了y的根节点
return false;
}
//查找x的根节点
private static int findRoot(int x) {
int t = x;
while(arr[t] != -1){
t = arr[t];
}
return t;
}
}