zoukankan      html  css  js  c++  java
  • 食物链(并查集经典题)

    食物链

    时间限制:1000 ms | 内存限制:65535 KB  

    poj-1182

    描述
    动物王国中有三类动物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

     

    这道题很经典,第一次做的带权的并查集,加深了我对并查集的理解与应用,去网上看的代码,有篇博客非常简洁,解题关键代码就只有7.8行的样子吧。没有很详细的注释,就这7,8行,看了我足足6.7小时,

    收获挺大,我想把这题关键的部分写下来,以后可以看看,顺便也许能帮到人吧。

    这是我参考的一篇博客:很详细,但是说得有点太多了,我都看得累了,代码也有点多了。。。下面的代码才是我说的只有7行关键的简洁代码,我写的类似与那篇博客

    http://blog.csdn.net/c0de4fun/article/details/7318642

    这道题的代码比较精巧,先说总体思路

    刚拿到这题,我想的是能不能将之化为3个并查集ABC,然后每个并查集头结点都记录着这类动物是什么属性,然后发现,根本行不通,因为不好确定到底是哪种属性的动物

    思路:这个的思路是将所有说到的动物都放在一个并查集,就是说只要有关系,就连在一起。

    1.find_head()找了头结点还压缩了路径到只有两层,这样就很容易判断两者的关系了,压缩路径时,就要适当的改变关系了

    2.如果是同一个头结点的话,对头结点的关系要推个式子,下面就说

    3.如果不在一个头结点,就说明没有关系嘛。肯定是真话,然后直接连起来就行了。连起来要又需要推导个式子,难住我很久。。。

     

    f[N]是存 N 号动物的父节点的编号,r[N]是对父节点的关系 0 同类,1 被父节点吃,2 吃父节点 

    find_head()函数递归不但找了头结点,还将搜索路径上的节点都压缩路径,将它直接连接到了头结点,对递归熟悉的不难理解

    关键在于确定对 爷爷节点 的那个公式上,穷举一下,然后就能很轻松的看出来的,关系有点像个三角形

     r[f[x]]   r[x]

    父亲对爷爷  儿子对父亲  儿子对爷爷 

       0      0       0      所以,儿子对爷爷就可以推出这个式子 (r[x] + r[f[x]] )%3  

       0      1        1   

       0      2       2   

       1      0       1  

       1      1       2  

       1      2       0  

       2      0       2  

       2      1       0     

       2      2       

    图:

    用这个式子就解决了压缩路径的关系转换

     

    然后到了将并查集联合这个函数,也就是 3 的情况,这是最难的部分:

    前面说了,用find_head()找头结点,并且直接连上头结点,然后判断两个的头结点是否相等,

      1,相等就判断关系是否矛盾,因为只有两层,枚举一下,容易发现规律,如果 (r[y]-r[x]+3)%3!=d-1  代表假话

    或者你足够理解那个find_head()的那个公式 3-r[x] 是 fx 对 x 的关系与 r[y] 相加一下就是 y 对 x 的关系看是否等于d-1。

      2,不同头结点就有点麻烦,像个四边形的关系,所以化成两个三角形。

        3-r[y]是 y 的头结点对 y 的关系,这个好理解吧,

        d-1是 y 对 x 的关系,因为 d 原本是 x 对 y 的关系,减 1 正好反过来。

    然后用上我们前面的find里面的那个对父节点的公式, (r[x] +r[f[x]] )%3  ,带进去,就得到 fy 对 x 的关系了,

    然后再加上 x 对 fx 的关系就得到了fy 对 fx 的关系, 这不就分成了2个三角形,得到了 r[fy] = 什么。,

    不懂,看图:已知标出,要求绿色,先求斜的,再求 r[fy]

     

    写完博客,发现更加理解这题了,也更加理解怎么推导公式啥的了,有收获。

     1 #include <stdio.h>
     2 
     3 int const N=50005;
     4 int f[N];
     5 int r[N];
     6 
     7 int find_head(int x)
     8 {
     9     int fx=x;
    10     if (x!=f[x])
    11     {
    12         fx=find_head(f[x]);
    13         r[x]=(r[x]+r[f[x]])%3;     //这是穷举可以轻松看出来的公式,压缩路径
    14         f[x]=fx;
    15     }
    16     return f[x];
    17 }
    18 
    19 int Union(int d,int x,int y)
    20 {
    21 
    22     int fx=find_head(x);
    23     int fy=find_head(y);
    24     if (fx!=fy)
    25     {
    26         f[fy]=fx;
    27         r[fy]=(3-r[y]+d-1+r[x])%3;      //很重要的一个公式,由find里面的公式推导过来
    28         return 0;
    29     }
    30     if ((r[y]-r[x]+3)%3!=d-1) return 1;
    31     return 0;
    32 }
    33 
    34 int main()
    35 {
    36     int n,k;
    37     int d,x,y;
    38     int ans=0;
    39     scanf("%d%d",&n,&k);
    40     for (int i=1;i<=n;i++)
    41     {
    42         f[i]=i;
    43         r[i]=0;
    44     }
    45     while (k--)
    46     {
    47         scanf("%d%d%d",&d,&x,&y);
    48         if (x>n||y>n||(x==y&&d==2)) 
    49         {
    50             ans++;
    51             continue;
    52         }
    53         if (Union(d,x,y)) ans++;
    54     }
    55     printf("%d
    ",ans);
    56     return 0;
    57 }
    View Code

     

  • 相关阅读:
    对象引用与对象克隆
    谁被回收了
    SpringMVC 返回json
    html基础
    org.hibernate.HibernateException: No Session found for current thread
    VB6之SendMessage模拟拖放事件
    VB6之GIF分解
    VB6之反编译工具VBRezQ
    VB6之WebBrowser控件
    (转载)VB6之鼠标移出事件
  • 原文地址:https://www.cnblogs.com/haoabcd2010/p/5688902.html
Copyright © 2011-2022 走看看