首先说并查集有三种
1、第一种是最简单的,没有权值
2、第二种是带权值的并查集
3、第三种是种类并查集(后面以食物链这道题来讲解)
每一种都是有模板的,要尽可能理解后才能长时间记忆
一、(first of all)
所有并查集由三部分组成-------主函数、寻找根节点函数(finds)、合并根节点函数(join)、以上三种只是在这三各部分中有所不同
finds函数
int finds(int x)
{
if(x!=v[x])
{
int y=finds(v[x]);
v[x]=y;//有这一行的目的是剪断一条长链的现状,相当于让他们各自都直接指向他们最后的根节点,而不是跟着题意一个一个的连接起来(长链形状)
return y;
}
return x;
}
join函数:
void join(int x,int y,int z)
{
int fx=finds(x);
int fy=finds(y);
if(fx!=fy)
v[fx]=fy;//如果他们的根节点不一样,就要把他们连接起来(是连接他们的根节点而不是连接他们自己)
//根节点相连接,就可以保证之前只想原根节点的都跟着更新了新的根节点
}
二、例题:http://acm.hdu.edu.cn/showproblem.php?pid=3038
finds函数
int finds(int x)
{
if(x!=v[x])
{
int y=finds(v[x]);
w[x]=(w[x]+w[v[x]]);//假如它1-->2的距离是3,2-->3的距离是3,那么1-->3的距离是不是等于3+3,这一行就是这个意思,此节点的权值
//加上根节点的权值,就是此节点到根结点的权值
v[x]=y;
return y;
}
return x;
}
join函数:
这一点就要涉及权值问题,在权值问题上面三角形是用来判断这一句话正确还是错误,四边形是用来判断在合并根节点时根节点之间的权值问题
1、三角形是当要合并的两个点的根节点一样的时候用来判断正确与否(根节点一样,就不需要合并,要检查之前的操作是否与现在的冲突)
此时的他们满足这个情况
我们就是要判断这个现在题中给出的x到y之间的权值,与之前给出的x到其根节点与y到其根节点而推出来的x到y之间的距离比较,不相等的话,那么这句话就错了。。。
比如2--->1之间的距离是4,3--->1之间的距离是4,那么2--->3之间的距离不就是0,(这里举的是事物对象,不要和数轴联系)
四角形就是用来为连接后的根节点赋值
此时,你已经知道x,y到其根节点之间的距离,题中一旦要让x与y合并,此时三条向量边,你就知道了三条,那不就可以求出来w[x]的值
此时你就用向量加减,如果方向相反就减去它
注意:你不能让fy指向fx,有的题目是不允许的,就比如给你区间【1,2】,和区间【3,4】的长度1,1,接着题目中再给出2到3的距离1,
按照fy指向fx则有:w[fy]=w[x]-z-w[y]就是一个负值,所以尽量都用fx---->fy
void join(int x,int y,int z)
{
int fx=finds(x);
int fy=finds(y);
if(fx==fy)
{
if(w[x]!=z+w[y]) ++k;
}
else
{
v[fx]=fy;
w[fx]=z-w[x]+w[y];
}
}
最后这道题要注意一点,你往join函数里面传值的时候,要将传之前y对应的那个变量值加一,要不然他们之间不会连起来
三、例题:食物链 http://poj.org/problem?id=1182
finds函数:
此时假设
0:同类
1:父吃子
2:子吃父
根据那个四边形w[fx]=3-w[x]+z+w[y] //3-w[x]的原因是w[x]箭头向上,要反向就要减去他,但是害怕减去之后总和为负值,所以就用3减去它
因为我们用的是fx--->fy,所以此时x指向的y也是父类,当输入的z为2的时候意思是x吃y,x吃y是子吃父,所以此时这个z不变
当输入z为1的时候,是同类的意思,而我们规定同类是0,所以我们要让z减去1
int finds(int x)
{ if(x!=v[x])
{
int y=finds(v[x]);
w[x]=(w[x]+w[v[x]])%3; //取余于三保证它们之间的关系始终是0、1、2
v[x]=y;
return y;
}
return x;
}
join函数:
void join(int x,int y,int z)
{
int fx,fy;
fx=finds(x);
fy=finds(y);
if(fx==fy)
{
if(z==1 && w[x]!=w[y])
{
ans++;
return;
}
if(z==2 && (w[y]+2)%3!=w[x])
{
ans++;
return;
}
}
else
{
v[fx]=fy;
if(z==2)
w[fx]=(3-w[x]+2+w[y])%3;
else if(z==1) w[fx]=(3-w[x]+w[y])%3;
}
}
这个时候我们规定
0:同类
2:父吃子
1:子吃父
所以当z为2的时候减去一就符合,所以不用,将z等于1和z等于2分开来写
void join(int x,int y,int z)
{
int fx,fy;
fx=finds(x);
fy=finds(y);
if(fx==fy)
{
if(z==1 && w[x]!=w[y])
{
ans++;
return;
}
if(z==2 && (w[y]+1)%3!=w[x])
{
ans++;
return;
}
}
else
{
v[fx]=fy;
w[fx]=(3-w[x]+z-1+w[y])%3;
}
}
下面依次是两种方法的代码
1、
2、