历届试题 合根植物

问题描述

w 星球的一个种植园,被分成 m × n m\times n m×n 个小格子(东西方向 m m m 行,南北方向 n n n 列)。每个格子里种了一株合根植物。
这种植物有个特点,它的根可能会沿着南北或东西方向伸展,从而与另一个格子的植物合成为一体。

如果我们告诉你哪些小格子间出现了连根现象,你能说出这个园中一共有多少株合根植物吗?

输入格式

第一行,两个整数 m , n m,n m,n,用空格分开,表示格子的行数、列数 ( 1 < m , n < 1000 ) (1<m,n<1000) (1<m,n<1000)
接下来一行,一个整数 k k k,表示下面还有 k k k 行数据 ( 0 < k < 100000 ) (0<k<100000) (0<k<100000)
接下来 k k k 行,第 2 + k 2+k 2+k行两个整数 a , b a, b a,b,表示编号为 a a a 的小格子和编号为 b b b 的小格子合根了。

格子的编号一行一行,从上到下,从左到右编号。
比如: 5 × 4 5\times 4 5×4 的小格子,编号:
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ] \begin{bmatrix} 1 & 2 & 3 & 4\\ 5 & 6 & 7 & 8\\ 9 & 10 & 11 & 12\\ 13 & 14 & 15 & 16\\ \end{bmatrix} 15913261014371115481216

样例输入

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
样例说明:其合根情况参考下图(注意:6 也是一个连通子集)

在这里插入图片描述



—— 分割线 ——


分析:
本题的要求是求合根个数,如果存在从来没有接触过“并查集”这个概念的同学,我相信你读完题也许是一脸懵的,甚至可能连样例数据都没看懂。而要是了解过“并查集”的同学们,一定能知道,本题其实就是想让你求连通子集的个数,摆明了要用到并查集的知识。当然了,本题也可以用dfs的方法来遍历图以求连通分支数,不过我并不推荐,因为那样做相较于使用并查集的方法还是很浪费时间的。而像这种典型的并查集例题,利用并查集确实来得快一些,并且在逻辑上更容易让人理解。如果存在不懂并查集的相关知识的童鞋,建议先去看一下相关的基础知识,以加深对本题解的认识。☞并查集

首先看题目中给出的一些关键词,合根植物,连根现象。这实际上就是在暗示整个图中点与点之间的关系——要么同属于某个点集合,要么各自属于其他的某个点集合。这样一来,便能把题目的任务转换为“统计有多少个点集”。如果你能有这样的一个转换,我相信你就会觉得本题很简单了。

具体的步骤如下:
1.初始化所有的点,即每个点都是一个集合

for(int i=1;i<=n;i++)
		pre[i]=i;

2.每录入一对点,就将这两个点联合,直到录入完毕,就能将所有点所在的集合统计出(表现在并查集中,就是每个点都找到了能代表当前所在集合的代表点)

for(int i=0;i<line;i++)
	{
		cin>>x>>y;
		unite(x,y);
	}

3.通过一层循环来遍历每个点,对于每个点,我们都标记其代表点的索引为1。

for(int i=1;i<m*n;i++)
	a[find_root] = 1;

4.统计数组a中有多少个元素为1,该数据即反映了整个图中存在的点集的数量,也就是我们要求的答案了。

for(int i=1;i<=m*n;i++)
		if(a[i]) ans++;

需要注意的是:本题中的数组pre占用的内存似乎超过了我的PC的栈内存,故直接运行并没有成功,只有把MAX变量减小后才能在本机中运行了。但是这并不会影响其在OJ上的评判,即在蓝桥杯官网后台是能执行的。



—— 分割线 ——


下面直接给出本题的完整代码:

#include<iostream>
using namespace std;

const int MAX=1000010;			//数组的最大长度(即图中点个数的最大值)
int m,n;						//当前图的长宽规格
int pre[MAX];					//用于存放每个点的根节点

void init(int n)				//初始化函数
{
	for(int i=1;i<=n;i++)
		pre[i]=i;
}
int find_pre(int key)		   //寻找根节点函数
{
	if(pre[key]==key) return key;
	return pre[key]=find_pre(pre[key]);
}
void unite(int x,int y)		   //联合函数
{
	int rootx=find_pre(x);
	int rooty=find_pre(y);
	if(rootx!=rooty) pre[rootx]=rooty;
}

int main() 
{
	int x,y,line;
	cin>>m>>n>>line;
	init(m*n);
	for(int i=0;i<line;i++)
	{
		cin>>x>>y;
		unite(x,y);
	}
	int ans=0,a[MAX]={0};
	for(int i=1;i<=m*n;i++)
		a[find_pre(i)]=1;
	for(int i=1;i<=m*n;i++)
		if(a[i]) ans++;
	cout<<ans<<endl;
	return 0;
}


END


Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐