zoukankan      html  css  js  c++  java
  • POJ-1182 食物链 并查集(互相关联的并查集写法)

    题目链接:https://cn.vjudge.net/problem/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),输出假话的总数。

    思路

    思路有二

    1. 把某一动物转化为三个并查集
      一个集合为同类,一个是食物,一个是天敌
      这样来说更容易管理,也就更好写
      插入数据
      按照同类食物和天敌的集合合并即可
      注意a+n代表a元素的敌人;a+2×n代表a元素的食物
      find(a)find(b+n)意思是b的敌人在a的集合里 -> a类是b的敌人
      find(a+n)
      find(b+2*n)意思是b的食物在a敌人的集合里 -> a的敌人类是b的食物类
      检查数据
      然而这样来说内存就是一个大问题了
      现在来算一下吧,最大内存问题

    [Mem = 3*2*N*4 = 1.2e6 B = 1.2e3 kB < 1e4 kB ]

    看来没什么问题,一开始忘了算了,直接去了思路二,痛苦不堪
    2. 一个动物是一个并查集
    把根节点做一个标志,标志比其他的大就算是捕食者,取模即可
    然而不好写,半途改变思想了

    代码

    #include <cstdio>
    const int MAX=int(5e4);
    struct Node{
        int parent, rank;
        Node(int parent=0, int rank=1):
            parent(parent),rank(rank) {}
    }node[3*MAX+5];
    int find(int x){
        return (x==node[x].parent)?x:(node[x].parent=find(node[x].parent));
    }
    
    void join(int a, int b){
        a=find(a); b=find(b);
        if (a==b) return;
        if (node[a].rank==node[b].rank) node[a].rank++;
        if (node[a].rank>node[b].rank) node[b].parent=a;
        else node[a].parent=b;
    }
    
    int main(void){
        int ans=0, n, m, oper[3];
    
        scanf("%d%d", &n, &m);
        for (int i=1; i<=3*n; i++) node[i]=Node(i);
        for (int snt=0; snt<m; snt++){
            scanf("%d%d%d", &oper[0], &oper[1], &oper[2]);
            if (oper[1]>n || oper[2]>n) {ans++; continue;}
            if (oper[0]==2 && oper[1]==oper[2]) {ans++; continue;}
    
            // self-0*n, eater-1*n, food-2*n
            if (oper[0]==1){
                // if (vis[oper[1]] && vis[oper[2]] && find(oper[1])!=find(oper[2])) {ans++; continue;}
                if (find(oper[1])==find(oper[2]+n) || find(oper[1])==find(oper[2]+2*n)){ans++; continue;}
                join(oper[1], oper[2]);
                join(oper[1]+n, oper[2]+n);
                join(oper[1]+2*n, oper[2]+2*n);
            }else if (oper[0]==2){
                if (find(oper[1])==find(oper[2]) || find(oper[1])==find(oper[2]+2*n)){ans++; continue;}
                join(oper[1], oper[2]+n);
                join(oper[1]+n, oper[2]+2*n);
                join(oper[1]+2*n, oper[2]);
            }
        }printf("%d
    ", ans);
    
        return 0;
    }
    
    
    Time Memory Length Lang Submitted
    282ms 1528kB 1489 G++ 2018-02-16 16:28:58

    > 03-18 Update: 又写了一边题,发现并没有理解解法。重新思考,把要点详细说明一下
  • 相关阅读:
    算法: 整数中1出现的次数(从1到n整数中1出现的次数)
    健身:肩部训练
    算法: 字符串的排列
    不能浮躁,还是需要沉淀;
    算法:从上往下打印二叉树
    健身:手臂训练
    抛出错误
    记录错误
    调用栈
    try/except/finally
  • 原文地址:https://www.cnblogs.com/tanglizi/p/8456438.html
Copyright © 2011-2022 走看看