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

历届试题 合根植物

问题描述

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


青蛙跳杯子是蓝桥杯历届试题中的一道典型题目。题目要求模拟一个青蛙跳杯子的游戏,给定n个杯子和m次操作,每次操作可以选择一个杯子,并将其放置在当前位置前面或者后面的位置。最后,要求输出经过m次操作后,杯子从左到右的顺序。 下面是一个可能的Java代码实现: ```java import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); // 杯子的数量 int m = scanner.nextInt(); // 操作的次数 int[] cups = new int[n]; // 初始化杯子的初始顺序 for (int i = 0; i < n; i++) { cups[i] = i+1; } // 执行m次操作 for (int i = 0; i < m; i++) { int index = scanner.nextInt(); // 选择的杯子的位置 int direction = scanner.nextInt(); // 移动的方向,1表示前面,2表示后面 // 将选择的杯子移动到指定位置 if (direction == 1) { for (int j = index-1; j > 0; j--) { int temp = cups[j]; cups[j] = cups[j-1]; cups[j-1] = temp; } } else if (direction == 2) { for (int j = index-1; j < n-1; j++) { int temp = cups[j]; cups[j] = cups[j+1]; cups[j+1] = temp; } } } // 输出最终的杯子顺序 for (int i = 0; i < n; i++) { System.out.print(cups[i] + " "); } } } ``` 该代码首先读取输入的杯子数量n和操作次数m,然后创建一个数组用来保存杯子的顺序。从1到n依次填充数组的初始顺序。接下来,执行m次操作,根据输入的位置和方向调整杯子的顺序,最后输出调整后的杯子顺序。 需要注意的是,该代码没有进行输入合法性的验证,实际应用中可能需要对输入进行适当的验证和处理。
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

theSerein

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值