关于我对并查集的通俗理解:同类的为一个集合。
并查集的组成:
- find函数(用来查找自己的父节点)
- father数组(用来存储自己的父节点,
千万不要忘记初始化) - join函数(用来压缩路径,改变自己的父节点)
find函数:
int find(int x)
{
if(x!=father[x]) //如果x的根节点不是本身,递归继续查找
father[x]=find(father[x]);
return father[x];
}
换一种写法
int find(int x) //查找x的父节点
{
while(father[x] != x) //如果x的父节点不是自己,则说明找到的人不是父节点,继续寻找
x = father[x];
return x; //找到父节点并返回
}
join函数:
void join(int x,int y) //把x和y合并为同一个集合
{
int fx=find(x), fy=find(y); //x的父节点是fx,y的父节点是fy
if(fx != fy) //如果不相等,则fx是fy的父节点
father[fx]=fy;
}
带权并查集
用一个数组value表示这种权值关系,注意每次value数组要清零
带权并查集的组成与并查集大致一样,但代码上有一定的差别,见代码
find函数
int find(int x)
{
if(x==father[x])
return x;
int t=father[x];//t不能省,father[x]的值发生了变化
father[x]=find(father[x]);
value[x]+=value[t];//value求和,权值相加
return father[x];
}
join函数
当出现权值这种新的联系的时候,需要把y的集合即find[y]连向x
void join(int x,int y,int s) //s代表x与y之间的关系
{
int fx=find(x);
int fy=find(y);
if(fx!=fy)
{
father[fy]=fx; //把y的集合连向x的集合
value[fy]=value[x]-value[y]+s; //符合向量的加减
}
}
带权并查集的典型例题——食物链
题目
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。
Input
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
Output
只有一个整数,表示假话的数目。
Sample Input
100 7 1 101 1 2 1 2 2 2 3 2 3 3 1 1 3 2 3 1 1 5 5
Sample Output
3
解题思路:
1.给了n 个动物,k行数据,每行三个整数d,x,y;d代表说法,x,y代表动物,sum代表假话数量
2.有三个物种,设置三种关系,分别是x吃y,x被y吃,x和y同一物种。然后分别用0代表同类,1代表前面吃后面,2代表前面被后面吃
3.如果x或者y比n大,或者d==2时,x==y,则直接判定为假话,sum++
4.if d==1时,x与y相互吃的关系时,为假话,sum++;else,把关系列进去
5.if d==2时,同类的情况为假话,sum++,else,把关系列进去
籽涵想要的AC代码
千万不要忘记初始化
#include<iostream>
#include<stdio.h>
using namespace std;
int n,k,d,x,y,sum;//sum代表假话数量
int father[50003*3];
int find(int x)//查找
{
while(father[x]!=x)//父节点不为本身
x=father[x];
return x;
}
void join(int a,int b)//
{
int fa=find(a);
int fb=find(b);
if(fa!=fb)
father[fa]=fb;
}
int main()
{
// cin >> n >> k;
scanf("%d%d",&n,&k);
for(int i=1;i<n*3;i++)//初始化父节点为本身
father[i]=i;
while(k--)
{
// cin >> d >> x >> y;
scanf("%d%d%d",&d,&x,&y);
if((x>n||y>n)||(d==2&&x==y))//第二种和第三种情况
{
sum++;
continue;
}
if(d==1)//表示X Y 同类
{
if(find(x)==find(y+n)||find(y)==find(x+n))//假话,互相吃的关系
sum++;
else
{
join(x,y);
join(x+n,y+n);
join(x+2*n,y+2*n);
}
}
else if(d==2)
{
if(find(x)==find(y)||find(x+n)==find(y))
//假话:1同类 2
sum++;
else
{
join(x,y+n);
join(x+n,y+2*n);
join(x+2*n,y);
}
}
}
printf("%d\n",sum);
// cout << sum << endl;
return 0;
}