【蓝桥杯】 历届试题 合根植物(并查集)

历届试题 合根植物

问题描述
w星球的一个种植园,被分成 m * n 个小格子(东西方向m行,南北方向n列)。每个格子里种了一株合根植物。
这种植物有个特点,它的根可能会沿着南北或东西方向伸展,从而与另一个格子的植物合成为一体。
如果我们告诉你哪些小格子间出现了连根现象,你能说出这个园中一共有多少株合根植物吗?

输入格式
第一行,两个整数m,n,用空格分开,表示格子的行数、列数(1 接下来一行,一个整数k,表示下面还有k行数据(0 接下来k行,第2+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

样例说明:其合根情况参考下图(注意:6也是一个连通子集)
【蓝桥杯】 历届试题 合根植物(并查集)_第1张图片

样例输出
5



—— 分割线 ——



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

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

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

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

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

for(int i=0;i>x>>y;
		unite(x,y);
	}

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

for(int i=1;i

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

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

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



—— 分割线 ——



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

#include
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;
}

你可能感兴趣的:(蓝桥杯试题题解)