问题描述
w星球的一个种植园,被分成 m * n 个小格子(东西方向m行,南北方向n列)。每个格子里种了一株合根植物。
这种植物有个特点,它的根可能会沿着南北或东西方向伸展,从而与另一个格子的植物合成为一体。
如果我们告诉你哪些小格子间出现了连根现象,你能说出这个园中一共有多少株合根植物吗?
输入格式
第一行,两个整数m,n,用空格分开,表示格子的行数、列数(1
格子的编号一行一行,从上到下,从左到右编号。
比如: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的方法来遍历图以求连通分支数,不过我并不推荐,因为那样做相较于使用并查集的方法还是很浪费时间的。而像这种典型的并查集例题,利用并查集确实来得快一些,并且在逻辑上更容易让人理解。如果存在不懂并查集的相关知识的童鞋,建议先去看一下相关的基础知识,以加深对本题解的认识。点击此处以打开并查集的介绍。
首先看题目中给出的一些关键词,合根植物,连根现象。这实际上就是在暗示整个图中点与点之间的关系——要么同属于某个点集合,要么各自属于其他的某个点集合。这样一来,便能把题目的任务转换为“统计有多少个点集”。如果你能有这样的一个转换,我相信你就会觉得本题很简单了。
具体的步骤如下:
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;
}