历届试题 合根植物
问题描述
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
样例说明
其合根情况参考下图
解析:
首先,题目思路很清晰,就是找图中结点由几个联通块。
其次,本来想用dfs的,但是一看点数,我去,1000000,(dfs一般时间复杂度为O(n^2))算了,只能套并查集了。
再者,再次分析,用并查集也挺简单的,只需要返回一个count即树的个数就ok了。
想要深层次了解并查集,我找了一篇博客,可以访问以下链接:
https://blog.csdn.net/dm_vincent/article/details/7655764
最后,经过两次不同的比较,我发现BufferedReader真的要比Scanner要快。
话不多说,上代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
//合根植物
final static int maxn=1000000;
static int root[]=new int[maxn+5];
static int count=0;//树的数量
static int find(int i)//返回i的根节点
{
while(i!=root[i])
{
i=root[i];//找根节点 根节点的根是自己
}
return i;
}
static void union(int i,int j)
{
int ri=find(i);
int rj=find(j);
if(ri!=rj)
{
root[rj]=ri;//rj的根节点变为ri 从而实现合并
count--;//此时树的数量减1
}
return;
}
public static void main(String[] args) throws IOException {
int m,n,k;
BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));
//加快输入
String str=bfr.readLine();
String s[]=str.split(" ");
m=Integer.parseInt(s[0]);
n=Integer.parseInt(s[1]);
str=bfr.readLine();
s=str.split(" ");
k=Integer.parseInt(s[0]);
for(int i=1;i<=m*n;i++)//初始化,每一个结点的根结点都是自己
{
root[i]=i;
}
count=m*n;//开始时有多少个结点,就有多少棵树
for(int i=1;i<=k;i++)
{
str=bfr.readLine();
s=str.split(" ");
int a=Integer.parseInt(s[0]);
int b=Integer.parseInt(s[1]);
union(a,b);
}
System.out.println(count);
}
}
Scanner版:
import java.io.IOException;
import java.util.Scanner;
//字符串输入700多毫秒,Scanner输入1.4s,证明BufferedReader输入确实要优于Scanner!!!
public class Main {
//合根植物
final static int maxn=1000000;
static int root[]=new int[maxn+5];
static int count=0;
static int find(int i)//
{
while(i!=root[i]) i=root[i];//找根节点
return i;
}
static void union(int i,int j)
{
int ri=find(i);
int rj=find(j);
if(ri!=rj)
{
root[rj]=ri;
count--;
}
return;
}
public static void main(String[] args) throws IOException {
int m,n,k;
Scanner cin=new Scanner(System.in);
m=cin.nextInt();
n=cin.nextInt();
k=cin.nextInt();
for(int i=1;i<=m*n;i++) root[i]=i;
count=m*n;
for(int i=1;i<=k;i++)
{
int a=cin.nextInt();
int b=cin.nextInt();
union(a,b);
}
System.out.println(count);
}
}
两份代码都过了,现在展示BufferedReader的评测情况:
详细记录 |
|
最优版:加了路径压缩,与合并时小树加到大树上:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
//合根植物
final static int maxn=1000000;
static int root[]=new int[maxn+5];
static int size[]=new int[maxn+5];
static int count=0;//树的数量
static int find(int i)//返回i的根节点
{
while(i!=root[i])
{
root[i]=root[root[i]]; // 路径压缩,会破坏掉当前节点的父节点的尺寸信息,因为压缩后,当前节点的父节点已经变了
i=root[i];//找根节点 根节点的根是自己
}
return i;
}
static void union(int i,int j)
{
int ri=find(i);
int rj=find(j);
if(ri!=rj)
{
if(size[ri]>size[rj])//小树加到大树上,可以使树的深度较大树加小树上增长的更慢
{
root[rj]=ri;//rj的根节点变为ri 从而实现合并
size[ri]+=size[rj];
}
else
{
root[ri]=rj;
size[rj]+=size[ri];
}
count--;//此时树的数量减1
}
return;
}
public static void main(String[] args) throws IOException {
int m,n,k;
BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));//加快输入
String str=bfr.readLine();
String s[]=str.split(" ");
m=Integer.parseInt(s[0]);
n=Integer.parseInt(s[1]);
str=bfr.readLine();
s=str.split(" ");
k=Integer.parseInt(s[0]);
for(int i=1;i<=m*n;i++)//初始化,每一个结点的根结点都是自己
{
root[i]=i;
size[i]=1;
}
count=m*n;//开始时有多少个结点,就有多少棵树
for(int i=1;i<=k;i++)
{
str=bfr.readLine();
s=str.split(" ");
int a=Integer.parseInt(s[0]);
int b=Integer.parseInt(s[1]);
union(a,b);
}
System.out.println(count);
}
}
评测详情,与前面的比较:
详细记录
评测点序号 | 评测结果 | 得分 | CPU使用 | 内存使用 | 下载评测数据 |
---|---|---|---|---|---|
1 | 正确 | 16.67 | 296ms | 27.93MB | 输入 输出 |
2 | 正确 | 16.67 | 125ms | 27.96MB | VIP特权 |
3 | 正确 | 16.67 | 515ms | 64.77MB | VIP特权 |
4 | 正确 | 16.67 | 500ms | 64.00MB | VIP特权 |
5 | 正确 | 16.67 | 703ms | 45.46MB | VIP特权 |
6 | 正确 | 16.67 | 687ms | 56.96MB | VIP特权 |
最后,我想说的并查集真的一点都不难,而且平均时间复杂度就本题而言远小于O(n^2)!