并查集是一种用来维护集合的简单算法。因为其实现算法很简单,经常用于面试题。
其实现原理是构造一棵树,其中在一个集合的元素都会储存在一棵树中。如果两个集合合并,可以将集合A放在集合B的节点,达到两个集合合并后所有元素的根节点都为同一个值即可。
算法关键是构建一个函数find(),该函数用来找出该点的根节点的值。如果将A点加入到集合B中,只需将A点的根节点值修改成B集合根节点的值即可。
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int n , m;
int p[N];
int find(int x) {
//这里十分巧妙,在寻根的过程中顺便把沿途的点的父亲值附上根节点的值
//从而实现了路径压缩,使得每个节点直接指向该集合的根节点
if( x != p[x] ) p[x] = find( p[x] );
return p[x];
}
int main()
{
scanf("%d%d" , &n , &m );
//初始化,每个点的父亲都是自己
for(int i = 1 ; i < n ; i++) p[i] = i;
while(m--)
{
char op;
int a , b;
cin >> op;
cin >> a >> b;
if( op == 'M' )
{
//将b点的父亲作为a点的父亲,间接将两个区间合并、
//将a集合合并到b集合
p[find(a)] = find(b);
}
else if( op == 'Q' )
{
if( find(a) == find(b) ) cout << "Yes" << endl;
else cout << "No" << endl;
}
}
return 0;
}
作者:阿柴
链接:https://www.acwing.com/activity/content/code/content/596393/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
接下来是一道比较难的题:食物链
题目非常长,大概就是存在一条循环食物链,比如A 吃 B , B 吃 C , C 吃 A。我们假设用一个树来存储。
我们假设存在一条循环食物链,比如A 吃 B , B 吃 C , C 吃 E , E 吃 F,我们思考一下可以发现当我们已知两个动物的状态时,我们是可以推出第三个动物的状态。拿A,B,C来说,我们根据A 吃 B , B 吃 C ,可以知道C是吃A的。因为这是一条循环的食物链,知道两个动物比能推出第三个动物的状态。看回刚刚那个例子。
我们知道C是吃A的,而C也是吃E的,所以A和E是同类,也就是A和E能吃B,这个自己推推。此时,当我们用一棵树来存储的话,我们用某个点到根节点的距离来表示在该食物链中的关系。当某个动物K的距离模3为1,另外一个动物M的距离倘若模3位1,则为同类,如果M模3为0,说明M吃K;若M模3为2,则K吃M。
下面这张图是用来解释当两个集合合并后,两个集合元素之间的关系是什么。
#include<iostream>
using namespace std;
const int N = 50010;
int p[N] , d[N];
int n , m ;
int find(int x)
{
if( x != p[x] )
{
//如果按表层意思,是先存一下根节点的值
//深度分析递归过程可知,这里是开一个递归的头,并求得p[x]
int u = find(p[x]);
d[x] += d[ p[x] ];
p[x] = u;
}
return p[x];
}
int main()
{
scanf("%d%d" , &n , &m);
for(int i = 1 ; i <= n ; i++) p[i] = i;
int res = 0;
while( m-- )
{
int x , y , t;
scanf("%d%d%d" , &t , &x , &y);
int px = find(x) , py = find(y);
if(x > n || y > n) res ++;
else
{
//当询问是否为同级
if( t == 1 )
{
//表示说是同级,其实不是同级关系
if( px == py && ( d[x] - d[y] ) % 3 != 0 ) res++;
else if( px != py ) //表示没有在同一个集合
{
//将y作为x的父节点,将dx连到dy上
p[ px ] = py;
//由于连上去,所以px到py的距离是不知道的
//设为T,由于x和y为同级,则 (d[x] + T) % 3 = d[y] % 3
//所以求得T = d[y] - d[x]
d[ px ] = d[y] - d[x];
}
}
//当询问是否可吃
else
{
//如果x和y在同一集合并且y比x小1(都是模3之后)
//应该的式子:d[x] % 3 = d[y] % 3 + 1
if( px == py && (d[x] - d[y] - 1) % 3 ) res++;
else if( px != py )
{
p[ px ] = py;
//(d[x] + T) % 3 = d[y] % 3 + 1
//化简得:T = d[y] - d[x] + 1
d[ px ] = d[y] - d[x] + 1;
}
}
}
}
cout << res << endl;
return 0;
}
作者:阿柴
链接:https://www.acwing.com/activity/content/code/content/598066/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。