问题描述
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个连通分量。
随着输入,我们将他们连接起来。
每次将较小连通块(也就是这个块中结点的数量)连接到较大连通块上。
所以我们需要一个数组来记录连通块的结点数量。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int m,n;
m = in.nextInt();
n = in.nextInt();
int k = in.nextInt();
Ans ans = new Ans(m*n+1);
for(int i=1;i<=k;i++) {
int p = in.nextInt();
int q = in.nextInt();
ans.union(p, q);
}
System.out.println(ans.count());
}
}
class Ans{
private int count;
private int id[];
private int sz[];
public Ans(int n) {
count = n - 1;
id = new int[n];
for(int i=0;i<n;i++)
id[i] = i;
sz = new int[n];
for(int i=0;i<n;i++)
sz[i] = 1;
}
public int find(int p) {
int root = p;
while(root!=id[root]) root = id[root];
//这里用的是路径压缩的加权quick-union算法。
while(p!=id[p]) {
int temp = p;
p = id[p];
id[temp] = root;
}
return root;
}
public void union(int p,int q) {
int i = find(p);
int j = find(q);
if(i==j) return ;
if(sz[i]<sz[j]) {
id[i] = j;
sz[j] += sz[i];
}else {
id[j] = i;
sz[i] += sz[j];
}
count--;
}
public int count() {
return count;
}
}