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

历届试题 合根植物

问题描述

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也是一个连通子集)

2a3486c6249400cb24016a85c5f4a5c7.png

样例输出

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

{

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<

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值