zoukankan      html  css  js  c++  java
  • 并查集入门 POJ 1182(带权并查集)

    这是一个关于并查集的入门题目 主要是熟悉并查集这种数据结构 并查集顾名思义就是可以高效查询和合并的一种数据结构 (但是并不能用于分割)

    并查集的实现可以用数组操作

    用pa(parent)代表并查集内每个树的父节点 ra(rank)代表树的高度 查询和合并用函数完成

    int init(int n) //初始化
    {
        for(int i=0; i<n; i++)
        {
            pa[i]=i;
            ra[i]=0;
        }
    
    }
    int Find(int x) //查询
    {
        return pa[x]==x ? x : pa[x]=Find(pa[x]);
    }
    
    bool same(int x,int y)  //判断x和y两元素是否在一个集合内
    {
        return Find(x)==Find(y);
    }
    
    void unite(int x,int y)  //合并x和y所属的集合
    {
        x=Find(x);
        y=Find(y);
        if(x==y) return;
        if(ra[x]<ra[y])
            pa[x]=y;
        else
        {
            pa[y]=x;
            if(ra[x]==ra[y]) ra[x]++;
        }
    }

    在经过防止退化的高度优化和路径压缩后 其复杂度为O(α(n)),α(n)为Ackerman函数的反函数 ,比O(log(n))更快。

    下面是一个用并查集解决的简单题目

    Description

    动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。 
    现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。 
    有人用两种说法对这N个动物所构成的食物链关系进行描述: 
    第一种说法是"1 X Y",表示X和Y是同类。 
    第二种说法是"2 X Y",表示X吃Y。 
    此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 
    1) 当前的话与前面的某些真的话冲突,就是假话; 
    2) 当前的话中X或Y比N大,就是假话; 
    3) 当前的话表示X吃X,就是假话。 
    你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。 

    Input

    第一行是两个整数N和K,以一个空格分隔。 
    以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。 
    若D=1,则表示X和Y是同类。 
    若D=2,则表示X吃Y。

    Output

    只有一个整数,表示假话的数目。

    Sample Input

    100 7
    1 101 1 
    2 1 2
    2 2 3 
    2 3 3 
    1 1 3 
    2 3 1 
    1 5 5
    

    Sample Output

    3

    题意需要稍微一点理解 对于每一条询问 我们需要对每个动物建立三个状态:A B C 即i-A i-B i-C 然后根据种类一和种类二分类 每次分类之前判断是否跟原有的集合冲突 当出现冲突的时候 计数器自增。
    代码如下:
    //#include<bits/stdc++.h>   //poj不支持这个头文件
    #include <iostream>
    #include <cstdio>
    
    using namespace std;
    #define MAXN 100005
    int pa[MAXN],ra[MAXN],a[MAXN];
    
    int init(int n)
    {
        for(int i=1; i<n; i++)
        {
            pa[i]=i;
            ra[i]=0;
        }
    }
    
    int Find(int x)
    {
        return pa[x]==x ? x : pa[x]=Find(pa[x]);
    }
    
    bool same(int x,int y)
    {
        return Find(x)==Find(y);
    }
    
    void unite(int x,int y)
    {
        x=Find(x);
        y=Find(y);
        if(x==y) return;
        if(ra[x]<ra[y])
            pa[x]=y;
        else
        {
            pa[y]=x;
            if(ra[x]==ra[y]) ra[x]++;
        }
    }
    
    int main()
    {
        int n,k,ans=0,t,x,y;
        scanf("%d %d",&n,&k);
        init(3*n);                                               //用3n个空间储存
        while(k--) 
        {
            scanf("%d %d %d",&t,&x,&y);
            if(x>n || y>n)                                   //越界也是出错                  
            {
                ans++;
                continue;
            }
            if(t==1)                                               //x和y是第一种情况
            {
                if(same(x,y+n) || same(x,y+2*n))
                    ans++;
                else
                {
                    unite(x,y);
                    unite(x+n,y+n);
                    unite(x+2*n,y+2*n);
                }
            }
            else                                                 //x和y是第二种情况(x吃y)注意谁吃谁
            {
                if(same(x,y) || same(x,y+2*n))
                    ans++;
                else
                {
                    unite(x,y+n);
                    unite(x+n,y+2*n);
                    unite(x+2*n,y);
                }
            }
    
        }
        printf("%d
    ",ans);
        return 0;
    }
    AC代码
    啊对了 这个题目 千万别用cin cout 会TLE 哎 要养成看到数据大的题目改scanf printf 真的是偷懒就要gg 我debug好久才意识到。。。。


  • 相关阅读:
    观后感(追番记)...
    网络流24题
    动物园
    [HNOI2016]网络
    部落战争(最小路径点覆盖)
    P4313 文理分科(最小割)
    P2774 方格取数问题(最小割)
    P2472 [SCOI2007]蜥蜴(最大流)
    P1231 教辅的组成(最大流)
    最短路计数(SPFA× Dijkstra√)
  • 原文地址:https://www.cnblogs.com/youchandaisuki/p/8612146.html
Copyright © 2011-2022 走看看