1、并查集是什么
并查集是用来在一个集合中进行元素搜索,或者将集合合并的一种方法。主要通过数组来实现。
2、并查集的实现思路
我们可以将一个数组中每个单位存储的元素初始化为它的下标,代表初始时它的编号就是它自己。在之后的操作中,如果两个集合合并,那么就将其中一个集合编号所在的数组位置修改为另一个集合编号作为代表。这时,当我们搜索到这两个集合时,会发现它们的编号相同,即属于同一个集合。
3、代码实现
并查集主要有以下的几个操作
初始化
for(int i = 0;i < n;i++)//n为集合编号的最大值
num[i]= i;
查询操作
int find(int x)
{
int head = x;
while(num[head] != head)//查询所有集合中最终的编号
head = num[head];
while(x != num[x])//路径压缩,缩短下次搜索的时间
{
x = num[x];
num[x] = head;
}
return head;
}
合并操作
void join(int a,int b)//将两个集合合并
{
int a1 = find(a);
int b1 = find(b);
num[a1] = b1;
while(b1 != num[a])
{
a = num[a];
num[a] = b1;
}
return;
}
4、反集
在做题的时候有时候会遇到集合的两种关系,其中一种是属于同一个集合,这种我们直接合并即可。另外一种就是"对立"的关系。如下面例题中的情况,敌人的敌人是朋友,对于这种操作,我们可以使用反集,通过将x + N视作其敌人进行合并操作,最后敌人们自然就会合并到一起。
5、例题及AC代码
题目描述
1920年的芝加哥,出现了一群强盗。如果两个强盗遇上了,那么他们要么是朋友,要么是敌人。而且有一点是肯定的,就是:
我朋友的朋友是我的朋友;
我敌人的敌人也是我的朋友。
两个强盗是同一团伙的条件是当且仅当他们是朋友。现在给你一些关于强盗们的信息,问你最多有多少个强盗团伙。
输入格式
输入文件gangs.in的第一行是一个整数N(2<=N<=1000),表示强盗的个数(从1编号到N)。 第二行M(1<=M<=5000),表示关于强盗的信息条数。 以下M行,每行可能是F p q或是E p q(1<=p q<=N),F表示p和q是朋友,E表示p和q是敌人。输入数据保证不会产生信息的矛盾。
输出格式
输出文件gangs.out只有一行,表示最大可能的团伙数。
AC代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
using namespace std;
int num[10005];
int n,m;
int find(int x)
{
int head = x;
while(num[head] != head)
head = num[head];
while(x != num[x])
{
x = num[x];
num[x] = head;
}
return head;
}
void join(int a,int b)
{
int a1 = find(a);
int b1 = find(b);
num[a1] = b1;
while(b1 != num[a])
{
a = num[a];
num[a] = b1;
}
return;
}
int main()
{
char c,d,e,f;
int a,b;
cin>>n>>m;
for(int i = 0;i < 10005;i++)
num[i]= i;
while(m--)
{
cin>>c>>a>>b;
if(c == 'F')
join(a,b);
else
{
join(n + a,b);
join(n + b,a);
}
}
int ans = 0;
for(int i = 1;i <= n;i++)
if(num[i] == i)
ans++;
printf("%d\n",ans);
}