POJ2524 Ubiquitous Religions 暨并查集入门

题意

多组样例,每组输入样例n,m表示有n个人,有m对数据,接着是m对人,每一对人表示他们在一个阵营里,问最后这n个人中一共有几个阵营(没有出现的人算是一个单独阵营)

m和n都为0时表示结束

输入样例

10 9       //test1
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
10 4     //test 2
2 3
4 5
4 8
5 8
0 0

输出样例

Case 1: 1
Case 2: 7


分析:这是一道典型的并查集应用。


并查集最常见的是用树实现。一开始所有元素都自成一树,每次碰到一对,就把其中一个元素的祖先改成另外一个,也就是把他们放到一个树里。这是“并”。查的话就是找到元素的祖先,如果两个元素的祖先是一个元素,那么就是一个集合,否则就是两个集合。

需要注意的是并查集有两个优化。 一是按秩(rank)合并 二是路径压缩。 

按秩合并就是说在合并两个集合时,总是让较小深度的集合加入较深深度的集合。若两个集合深度相同,则让一个加入另一个,同时rank++。

路径压缩就是为了避免找一个元素的祖先时经过的路径过长,数的深度越小越好(深度为2时正好查询复杂度为O1)。所以,在找一个元素的祖先的时候,经过的元素肯定都是这个元素祖先的元素。所以在找的同时不妨都把它们连到祖先上。

下面放并查集模板。

int par[maxn]; //元素的祖先
int dep[maxn]; //树的深度

//初始化n个元素
void _init(int n)
{
    for(int i = 0 ; i < n ; i ++){
        par[i] = i;     //各自成树
        dep[i] = 1;     //各个树的深度都为1
    }
}

//查询
int _find(int x)
{
    if(par[x] == x) return x;
    else return par[x] = _find(par[x]); //路径压缩   利用栈的特性,把路径上所有元素都变成祖先的儿子
}

//合并
void _unite(int x,int y)
{
    x = _find(x); //找到x的祖先
    y = _find(y); //找到y的祖先
    if(x == y) return; //如果祖先相同,那么不用合并
    if(dep[x] < dep[y]) par[x] = y; //合并到深度较高的树
    else{
        par[y] = x;
        if(dep[x] == dep[y]) dep[x] ++; //如果两树深度相同 dep++
    }
}


到这里这题就自然解出了,最后扫一遍判断有多少树就可以了(par[i] = i 则说明i是一个树的祖先)。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值