并查集
所谓并查集,类似于找朋友
现在有一群人(总共n个人),两两之间组成了朋友。
而且朋友的朋友也是自己的朋友(比如A和B是朋友,A和C也是朋友,那么A,B,C他们三个就互为朋友。)
而由于我们给数据时只会给K个数,代表这K个人是朋友(一般K的值是2)
并查集作用
1.用来看某个人A和另一个人B是不是朋友,
2.用于查看n个人中,组成了几个朋友圈(两个朋友圈就代表,这两组人互不认识)。
3.有时候求N个城市还需要修几条路才能完完全畅通
4. … …
举实例说明
杭电oj1232:畅通工程
Problem Description
某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
Input
测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
注意:两个城市之间可以有多条道路相通,也就是说
3 3
1 2
1 2
2 1
这种输入也是合法的
当N为0时,输入结束,该用例不被处理。
Output
对每个测试用例,在1行里输出最少还需要建设的道路数目。
Sample Input
4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0
Sample Output
1
0
2
998
思路
首先,这道题符合并查集作用3。
想要知道还需要建几条路,我们只需要知道现在有几个“朋友圈”即可,比如现在有n个“朋友圈”。那么我们只需要再修建(n-1)条路就可以了。
===========================================================================================================
并查集代码
我们拆分一下:
第一部分:求祖先
我们为每一个人设置一个“祖先”per[i]。
也就是说我们在一个朋友圈选一个祖先A,只要和A是直接或者间接是朋友的我们都把他的祖先定为A,也就是说per[i]=A,per[j]=A。
int father(int a) //寻找祖先,利用递归
{
if(per[a]==a)
return a;
else
{
per[a]=father(per[a]);
return per[a];
}
}
第二部分:查看是否属有相同祖先
现在新输入2个人,他们两个有关系。我们就要看看他本来在不在一个圈子里。不在的话就和成一个圈子
如果i和j的祖先相同,即per[i]==per[j]
那么i和j就在一个朋友圈。如果他们不在一个朋友圈,可是现在他们两个成朋友了所以需要合成一个朋友圈
void join(int x,int y) //合并朋友圈
{
int root1,root2;
root1=father(x);
root2=father(y);
if(root1!=root2)
per[root1]=root2;
}
那么我们来解决一下这道题
#include<bits/stdc++.h>
using namespace std;
int per[1010];
void sta(int n) //初始化
{
for(int i=1;i<=n;i++)
per[i]=i;
}
int father(int a) //找祖先
{
if(per[a]==a)
return a;
else
{
per[a]=father(per[a]);
return per[a];
}
}
void join(int x,int y) //合并朋友圈
{
int root1,root2;
root1=father(x);
root2=father(y);
if(root1!=root2)
per[root1]=root2;
}
int main()
{
int m,n,a,b;
while(scanf("%d",&n)&&n)
{
sta(n);
scanf("%d",&m);
while(m--)
{
scanf("%d %d",&a,&b);
join(a,b);
}
int sum=0;
for(int i=1;i<=n;i++) //祖先是自己一定代表有一个朋友圈
{
if(per[i]==i)
sum++;
}
printf("%d\n",sum-1);
}
return 0;
}