关闭

并查集(擒贼先擒王)

标签: 并查集
577人阅读 评论(0) 收藏 举报

并查集是若干个集合,可以判断两个对象是否在一个集合中,从而判断它们需不需要进行一个操作。

每个集合都是一棵树(注意不一定是二叉树),这里说一下二叉树和其他树的一点区别,二叉树编号是按位置来的(一棵二叉树一开始是怎样就一直是怎样,变的只是每个节点的值,编号永远不会改,父亲节点也永远不会改),而其他树的编号是按新建节点的顺序来的,越前添加的新节点编号越小(这个编号可以移动,比如某个节点的父亲节点可以变成它的父亲节点的父亲节点,这也就是并查集的一种操作)。

比如看一道例题

有若干个强盗 有若干个线索,每个线索中的两个强盗为同伙(倘若A与B是同伙,B与C是同伙,那么A与C也是同伙),判断有多少个独立的犯罪团伙。

样例数据:

10 9//10个强盗 9条线索

1 2

3 4

5 2

4 6

2 6

8 7

9 7

1 6

2 4

显然,在线索输入之前,每个强盗都是一个犯罪团伙,每次读入一个线索,就将为同伙的两个强盗的团伙合并为一个团伙。

为了方便,每个团伙都会有一个“老大”,至于有什么用,待会就知道了,显然一开始每个强盗都是自己团队的老大,合并的时候需要找到两个团伙的老大,然后将其中一个老大的上司更新为另一个老大。

fa[i]代表第i个强盗的直接上司的编号,注意是直接上司,它的直接上司有可能还有上司,直到它的上司的上司的上司……(若干个上司)的上司仍是自己的时候,这个强盗才是这个团伙真正的老大,显然,最后只需要判断,有多少个强盗的老大就是他自己,就是最终答案。

上代码

#include<cstdio>
using namespace std;
int fa[500];
void together(int x,int y){
	fa[x]=y;//y变成了x团伙的老大的老大 
	return ;
}
//有些代码还查询x与y的树的深度 再判断是x合并到y还是y合并到x更优化 而这里有将节点直接接到根节点的优化 所以循环不会太多 理论上应该不会超过3次 
int dfs(int h){
	if(fa[h]==h)return h;
	return fa[h]=dfs(fa[h]);
}
/*
这里函数很简单,但是有个很重要的优化。
如果一个强盗的上司是他自己说明他就是老大 那么直接返回他自己(这个很简单)
而当一个强盗的上司不是他自己时,就将他的上司赋值为他的上司的老大,比如说一个强盗的上司的上司的上司......(200个上司后)的上司是老大 那么如果再次查询这个点又要循环200次 而如果直接赋值成老大 那么他只需要向上搜一次 即使将这个团伙合并到别的团伙(即他的老大的上司被赋值了)也只需要搜两次 
*/
int main(){
	int i,j,k,m,n,x,y,ans=0;
	scanf("%d %d",&n,&m);
	for(i=1;i<=n;i++)fa[i]=i;//当读入线索之前,每个团伙的强盗都只有一个,每个强盗都是自己团伙的老大 
	for(i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		together(dfs(x),dfs(y));//dfs[h]代表第h个强盗的老大 
	}
	for(i=1;i<=n;i++){
		if(fa[i]==i)ans++;
	}
	printf("%d",ans);
	return 0;
}


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:29424次
    • 积分:497
    • 等级:
    • 排名:千里之外
    • 原创:19篇
    • 转载:12篇
    • 译文:0篇
    • 评论:1条
    文章分类
    文章存档
    最新评论