前几天学长跟我们讲了并查集的原理和方法,我又看了一些博客,做了几道模板题,感觉还不错,来简单介绍一下并查集的一些东西
并查集,顾名思义就是合并、查找的一系列操作。
当今社会人脉极为重要,我们出门在外很多时候靠的就是朋友互帮互助,当你和一个不认识的人发生了矛盾,因为考虑到经济原因不知道要不要动手的时候,你们突然发现有一个共同的朋友B,朋友的朋友就是朋友,之后化干戈为玉帛。当然,由于朋友太多,有时候你和你的对手没有办法一个一个的确定有没有共同的朋友,这时候就需要确定一个声望高权利大的人作为自己的牌面,你说可是跟A市长混的,你算老几?对方一听,我也跟A市长混,哈哈,误会误会,之后两人手牵手去喝酒。但让如果对面说S市长是谁?不认识,我只认识B市长。那就不用考虑那么多了,骂他就完事了,毕竟谁先动手谁完蛋。
以上只是简单介绍了“查找”,那么如何合并呢?
你在路上和一个人发生了矛盾,你只认A市长,但是对面是P省长的人,这时你的老大A市长感觉和省长发生矛盾很不划算,想法设法邀请到家里吃顿便饭,因为省长的职位更大,所以市长以后跟P省长混,之前市长手下的所有人都算是跟P省长混,那么你的老大自然也变成了P省长,以后出门只需要说我跟P省长混!
下面来看一下代码
int find(int x)
{
int r = x;
while(pre[r]!=r)
{
r = pre[r]; //直到找到老大为止,重所周知,老大只跟自己混
}
//路径压缩
int a,b,c;
a = x;
while(a!=r)
{
b = pre[a];
pre[a] = r;
a = r;
}
return r;
}
关于路径压缩
还看我们上面的例子,如果A市长偷偷摸摸地跟了P省长,没有声张,当以后A市长的人和别人发生了矛盾,每次都会找到A,因为他们还以为他们的老大是A,A去和对面的人说我老大是P省长,表示他们都是P的人,每次如此,A市长也不耐烦了,跟下面的人公开了,说以后你们的老大是P省长,出去混,不要再提我了!直接提P老大的名字就好了!
int join(int x, int y)
{
int a =find(x);
int b= find(y);
if(a!=b)
{
//这里可以加个判断条件,可以是谁的值大谁当老大或者是其他条件
pre[a] = pre[b];
}
}
这里可以看一下HDU的一道题 HDU畅通工程
AC代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
int pre[1010];
int find(int x)
{
int r = x;
while(pre[r]!=r)
{
r = pre[r];
}
//路径压缩
int a,b,c;
a = x;
while(a!=r)
{
b = pre[a];
pre[a] = r;
a = r;
}
return r;
}
int join(int x, int y)
{
int a =find(x);
int b= find(y);
if(a!=b)
{
pre[a] = pre[b];
}
}
int main()
{
int m,n;
int a,b;
int s[10000];
while(cin>>n>>m)
{
int sum = 0;
memset(s,0,sizeof(s));
for(int i=1;i<=n;i++)
pre[i] = i;
while(m--)
{
cin >>a>>b;
join(a,b);
}
for(int i=1;i<=n;i++)
{
s[find(i)]=1;
}
for(int i=0;i<=n;i++)
sum+=s[i];
cout<<sum-1<<endl;
}
return 0;
}