• 【并查集】食物链(带权并查集)


    问题 A: 食物链

    时间限制: 1 Sec  内存限制: 128 MB

    题目描述

            动物王国中有三类动物  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),输出假话的总数。

    输入

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

    输出

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

    样例输入

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

    样例输出

    3

    提示

    100 7

    1 101 1    假话 

    2 1 2      真话 

    2 2 3      真话 

    2 3 3      假话 

    1 1 3      假话 

    2 3 1      真话 

    1 5 5      真话 
     
     
    【分析】
     1.如果a吃b,b吃c,c吃d,那么a和d为同类;
     2.如果a吃b,c也吃b,那么a和c也为同类;
     显而易见,这道题目就可以用“并查集”来完成。
     
     如果x吃y则有一条x指向y边权为1的边,如果x被y吃则有一条x指向y边权为2的边;
     处理:如果x y在一个集合则描述判断是否合法,如果x y不在一个集合则合并两个集合;
     
     1 #include<iostream> 
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 using namespace std;
     6 int p[50005],f[50005];  //p mean the father of i;
     7                         //f mean the length of i to its father;
     8 int father(int x)       //find its father
     9 {
    10     if (x!=p[x]) 
    11     {
    12         int temp=p[x];
    13         p[x]=father(p[x]);
    14         f[x]=(f[x]+f[temp])%3;
    15     }
    16     return p[x];
    17 }
    18 int main()
    19 {
    20     int n,k,d,x,y,count=0;
    21     scanf("%d%d",&n,&k);
    22     for (int i=1;i<=n;i++)
    23     {
    24         p[i]=i; f[i]=0;
    25     }   //firstly
    26     for (int i=1;i<=k;i++)
    27     {
    28         scanf("%d%d%d",&d,&x,&y);
    29         if ((d==2&&x==y)||(x>n||y>n))  
    30          {
    31              count++;
    32              continue;
    33          }
    34         if (d==1)
    35         {
    36             if (father(x)==father(y))
    37              {
    38                  if (f[x]!=f[y]) count++;
    39              }
    40              else 
    41              {
    42                  f[p[x]]=(f[y]-f[x]+3)%3;
    43                  p[p[x]]=p[y];
    44              }
    45         }
    46         if (d==2)
    47         {
    48             if (father(x)==father(y))
    49              {
    50                  if (f[x]!=((f[y]+1)%3)) count++;
    51              }
    52           else
    53             {
    54                f[p[x]]=(f[y]-f[x]+4)%3;
    55                p[p[x]]=p[y];
    56              }
    57         }
    58     }
    59    printf("%d",count);
    60     
    61 }

    一段时间后再回来研究这道题,并且要学习一下带权并查集

    (之前还有一个并查集专题,也可以再回顾一下啊)

    带权并查集

    带权值的并查集只不过是在并查集中加入了一个value[ ]数组
    value[ ]可以记录很多种东西,不一定是类似距离这种东西,也可以是相对于根节点的状态
    加入了权值,函数应该有一些改变
    function find(x:longint):longint;
    var tmp:longint;
    begin
        if father[x]=x then exit(x);
        tmp:=father[x];
        father[x]:=find(father[x]);
        //下面一句可以随意改
        value[x]:=value[tmp]+1;
        exit(father[x]);
    end;

    -知识点完-

    接下来上食物链题解(简单粗暴的题解)

    var
    n,m,i,ans:longint;
    father,size:array[0..50000]of longint;
    function find(x:longint):longint;
    var t:longint;
    begin
        if father[x]=x then exit(x)
            else
                begin
                    t:=father[x];
                    father[x]:=find(father[x]);            //路径压缩
                    size[x]:=(size[x]+size[t]) mod 3;   
                    //根据找规律可得,各种情况两两结合可得size
                    exit(father[x]);
                end;
    end;
    function judge:boolean;
    var kind,x,y,fx,fy:longint;
    begin
        //以下的各种判定可用向量的规律判断得
        readln(kind,x,y);
        if (x>n)or(y>n) then exit(false);
        if (kind=2)and(x=y) then exit(false);
        fx:=find(x);     fy:=find(y);
        if fx=fy then    
            begin
                if (size[y]-size[x]+3) mod 3<>(kind-1) then exit(false) 
                    else exit(true);
            end
        else
            begin
                father[fy]:=fx;
                size[fy]:=(size[x]-size[y]+kind-1+3) mod 3;
                exit(true);
            end;
    end;
    begin
        readln(n,m);
        for i:=1 to n do
            begin
                father[i]:=i;
                size[i]:=0;
            end;
        for i:=1 to m do 
            if judge=false then ans:=ans+1;
        writeln(ans);
    end.
  • 相关阅读:
    IOS开发学习笔记020-练习总结
    IOS开发学习笔记019-动态创建控件
    IOS开发学习笔记018- 一般控件的使用
    IOS应用程序开发流程
    IOS开发学习笔记017-第一个IOS应用
    IOS开发学习笔记016-Foundation框架
    IOS开发学习笔记015-block和protocol
    IOS开发学习笔记014-ARC机制
    IOS开发学习笔记013-内存管理
    IOS开发学习笔记012-核心语法
  • 原文地址:https://www.cnblogs.com/Hathawaxy/p/7217123.html
Copyright © 2020-2023  润新知