poj1182----并查集

本文介绍了并查集这一数据结构的基本概念与实现方法,并通过一道具体题目详细讲解了并查集的应用技巧,特别是如何利用并查集解决集合间关系问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文地址:http://www.cnblogs.com/ffjjqqjj/archive/2011/07/20/2112226.html


做的第一道并查集题。

这题揪心一整天了,网上搜了一下题解才大概明白,明白了之后感觉思路还算是简单,就是集合间关系的公式的推导很重要。


/******************************************************************************

顺便了解了一下并查集。

     并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并查询问题。常常在使用中以森林来表示。

  集就是让每个元素构成一个单元素的集合 ,也就是按一定顺序将属于同一组的元素所在的集合合并。
 初始化
  把每个点所在集合 初始化为其自身。
  通常来说,这个步骤在每次使用该数据结构 时只需要执行一次,无论何种实现方式,时间复杂度 均为O(N)。

查找
  查找元素所在的集合,即根节点。

合并
  将两个元素所在的集合合并为一个集合。

  通常来说,合并之前,应先判断两个元素是否属于同一集合 ,这可用上面的“查找”操作实现。

**************************************************************************************************/


p(x)为x所属树的根节点。kind(x)为x与该树根节点的关系。height(x)为树x的高度。

现在规定:
0:同类关系。
1:吃关系。
2:被吃关系。
我们用x--r-->y表示x和y之间的关系是r,比如x--1--y代表x吃y。现在,若已知x--r1-->y,y--r2-->z,如何求x--?-->z?,于是我们不难发现,
x--(r1+r2)%3-->z。有了这个结论,我们就可以做题了。
我们初始化kind(1-N)=0,表示自己和自己为同类。P(1-N)=i,代表各个节点都是一棵树。当D X Y时,则应合并X的根节点和Y的根节点,同时修改各自的kind。那么问题来了,合并了之后,被合并的根节点的kind值如何变化呢?
现有x和y,d为x和y的关系,px和py分别是x和y的根节点,于是我们有x--kind[x]-->px,y--kind[y]-->py,显然我们可以得到px--(3-kind[x])-->x,py--(3-kind[y])-->y。假如合并后px为新的树的根节点,那么原先px树上的节点不需变化,py树则需改变了,因为kind值为该节点和树根的关系。这里只改变kind(py)即可,因为在进行find_set操作时可相应改变py树的所有节点的kind值。于是问题变成了py--?-->px。我们不难发现py--(3-kind[y])-->y--(3-d)-->x--kind[x]-->px,根据前面的结论,我们有py--(3-kind[y]+3-d+kind[x])%3-->px。我们求解了px和py的关系了。

代码如下:

#include<stdio.h>
int father[50001];
int kind[50001];
int height[50001];
int Find(int x)
{
    if(father[x] == x) 
        return x;
    int t;
    t = father[x];
    father[x] = Find(father[x]);
    kind[x] = (kind[x] + kind[t]) % 3;
    return father[x];
}
void Union(int px, int py, int x, int y, int d)
{
    if(height[px] < height[py]){
        father[px] = py;
        kind[px] = (3-kind[x]+d+kind[y]) % 3 ;
    }
    else{
        if( height[px] == height[py] )
            height[px]++;
        father[py] = px;
        kind[py] = ( 3 - kind[y] + 3 - d + kind[x]) % 3;
    }
}
int main()
{
    int i,sum,n,t,d,x,y,px,py;
    scanf("%d %d", &n, &t);
    for(i=1; i<=n; i++)
    {
        father[i] = i;
        kind[i] = height[i] = 0;
    }
    sum = 0 ;
    while(t--)
    {
        scanf("%d%d%d", &d, &x, &y);
        if(x>n || y>n ) {
            sum++;
            continue;
        }
        if( d==2 && x==y) {
            sum++;
            continue;
        }
        px = Find(x);
        py = Find(y);
        if(px == py) {
            if(d==1 && kind[x]!=kind[y]) {
                sum++;
                continue;
            }
            if(d==2 && (kind[x]+3-kind[y])%3 != 1){
                sum++;
                continue;
            }
        }else{
            Union(px, py, x, y, d-1);
        }
    }
    printf("%d\n", sum);
    getchar();
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值