ACM——并查集

并查集概述

并查集是一种树型的数据结构,用于处理不相交集合的合并及查询问题。
并查集主要分为两种操作:查找和合并。
(1)查找元素的祖先,为降低复杂度,首先进行路径压缩,即找到最久远的祖先时“顺便"把它的子孙直接连接到他的孩子处,这样就避免了树的结构退化,提高效率。

int getFather(int u){
if(father[u]!=u
	father[u]=getFather(father[u]);
	return father[u];
}

(2)将两个不相交的集合合并,类似于两个数的合并。

void Union(int x,int y){
	int fx=getFather(x),fy=getFather(y);
	if(fx!=fy){
		father[fx]=fy;
	}
}

1、POJ 1988 Cube Stacking

题目链接:https://vjudge.net/problem/POJ-1988在这里插入图片描述

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 30010

int n,fa[N],r[N],mx[N];
//r[x]表示x到根节点的距离,mx[x]表示这个集合中有多少个盒子 
void init(){
	int i;
	for(i=1;i<N;i++)
		fa[i]=i,r[i]=0,mx[i]=1;
} 

int find(int x){	//查找并查集的祖先 
	int fx=fa[x];
	if(fa[x]!=x){//自己是自己的祖先即是根 
		fx=find(fa[x]);
		r[x]+=r[fa[x]];
	}
	return fa[x]=fx;
}
void U(int x,int y){	//将两个集合合并 
	int fx=find(x),fy=find(y);
	fa[fy]=fx;
	r[fy]+=mx[fx];	//合并时更改相应值 
	mx[fx]+=mx[fy];
}
int main(){
	char op[3];
	int i,j;
	while(scanf("%d",&n)!=-1){
		init();
		while(n--){
		scanf("%s",op);
		if(op[0]=='C'){
			scanf("%d",&i);
			int f=find(i);
			printf("%d\n",mx[f]-r[i]-1);	//该集合盒子个数减去在i盒子之上的个数 
		}
		else{
			scanf("%d%d",&i,&j);
			U(i,j);
		}
	}
	}
return 0;	 
}

POJ1182 食物链(还不太懂)

题目链接:
https://vjudge.net/problem/POJ-1182
参考:
https://www.cnblogs.com/zhuanzhuruyi/p/5863738.html
https://blog.csdn.net/qq_39520417/article/details/81569982
在这里插入图片描述

/*
不同树合并且更新关系(x树做主根),如果 x和y为关系r1, y和z为关系r2, 那么x和z的关系就是(r1+r2)%3
如果 d1则x和y是同类 ,那么 y对x的关系是0,如果d2 ,则x吃了y, 那么y对x的关系是1, x对y的关系是2。综上所述 ,无论d为1或者是为2, y对x的关系都是 d-1。
fy对y 的关系为 3-r[y] (有点互补的感觉,注意这里是不同类喔)
y对x的关系为 d-1,
x 对fx 的关系为 r[x]
所以fy对fx 的关系是(3-r[y] + d-1 + r[x])%3。
*/ 


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 50010

int fat[maxn];//存父节点
int ran[maxn];//存与父节点的关系,0同一类,1被父节点吃,2吃父节点 

void init(int n){
	for(int i=0;i<=n;i++){
		fat[i]=i;
		ran[i]=0;
	}
	return;
}
int find(int x){
	if(x==fat[x])
		return fat[x];
	int y=find(fat[x]);
		ran[x]=(ran[x]+ran[fat[x]])%3;//递归后从祖先节点向后到每个孩子来计算 
	return fat[x]=y;//路径压缩 
}

int U(int d,int x,int y){//区间合并与查询 
	int fx=find(x),fy=find(y);
	if(fx==fy){//共父节点才能判断出关系 
	if((ran[x]-ran[y]+3)%3==d-1)
		return 0;
	return 1; 
	}		
	fat[fx]=fy;//连接两父节点
	ran[fx]=(-ran[x]+d-1+ran[y]+3) %3;//注意处理负数情况
	return 0; 
} 

int main(){
	int n,k,a,x,y;
	scanf("%d %d",&n,&k);
	init(n);
	int ans=0;
	while(k--){
		scanf("%d %d %d",&a,&x,&y);
		if(x==y&&a==2)ans++;
		else if(x>n||y>n)ans++;
		else
			ans+=U(a,x,y);		
	}
	printf("%d\n",ans);
	return 0;
} 
发布了54 篇原创文章 · 获赞 26 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_43629813/article/details/98655427