zoukankan      html  css  js  c++  java
  • POJ1182食物链

    公认的并查集经典。题目不贴了,链接在这里http://poj.org/problem?id=1182

    并查集的基础功能是 判断两个元素是否属于同一个集合 和 合并元素到同一个集合。

    更高的层次的应用是 判断和维护两个元素的关系。

    数组Fa[ x ]表示x所在集合的根节点,Rank[ x ]表示x和x所在集合根节点的关系。

    这篇博客深入的解释了这道题目,图文并茂,强烈推荐 

    https://blog.csdn.net/niushuai666/article/details/6981689

    向量的思想实在是惊艳,一下子就把这个问题完美的刻画出来。

    题目说“动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A”。

    这构成一个循环,与取模运算有很大的相似性。所以我们做出如下假设:

    Rank[ x ]=0表示x和Fa[ x ]同类
    Rank[ x ]=1表示Fa[ x ]吃x
    Rank[ x ]=2表示Fa[ x ]被x吃

    这样假设之后,关系就能够通过加减进行计算。

    例如:

    如果A吃B, B吃C,那么按照向量的计算,A和C的关系就是AB加BC,即AC=AB+BC,按照上面假设等于2,结果就是A被C吃

    如果A吃B,A吃C,BC=AC-AB=0,B和C是同类

    .......

    这也解释了为什么输入的D要减一

    之前有个疑惑Find函数为什么能够维护正确的关系。仔细想,发现递归真是奇妙。

    详细的看一下这段代码

    int Find(int e)
    {
        if(Fa[e]==e)return e;
        int oldF=Fa[e];//保存当前节点的父节点
        Fa[e]=Find(Fa[e]);//当前节点指向根节点
        Rank[e]=(Rank[oldF]+Rank[e])%3;
      //父节点的值已经在上层函数中被更新,Rank[oldF]表示的是oldF和根节点的关系
      //Rank[e]还是保持着e和oldF的关系
      //执行更新语句之后,Rank[e]表示e和根节点的关系
    return Fa[e]; }

    代码:

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<string>
    #include<vector>
    #define DEBUG(x) cout<<#x<<" = "<<x<<endl
    using namespace std;
    const int MAXN=5e4+10;
    int N,K;
    int Fa[MAXN];
    int Rank[MAXN];
    ///Rank表示关系
    ///0表示x和Fa[x]同类
    ///1表示Fa[x]吃x
    ///2表示Fa[x]被x吃
    int Find(int e)
    {
        if(Fa[e]==e)return e;
        int oldF=Fa[e];
        Fa[e]=Find(Fa[e]);
        Rank[e]=(Rank[oldF]+Rank[e])%3;
        return Fa[e];
    }
    void Union(int a,int b,int r)
    {
        int t1=Find(a);
        int t2=Find(b);
        if(t1!=t2){
            Fa[t2]=t1;
            Rank[t2]=(Rank[a]+r+3-Rank[b])%3;
        }
    }
    void Init()
    {
        for(int i=0;i<=N ;i++ ){
            Fa[i]=i;
            Rank[i]=0;
        }
    }
    int main()
    {
    //    freopen("in.txt","r",stdin);
        int cnt=0;
        scanf("%d%d",&N,&K);
        Init();
        while(K--){
            int D,X,Y;
            scanf("%d%d%d",&D,&X,&Y);
            if(X>N||Y>N){cnt++;continue;}
            if(D==2&&X==Y){cnt++;continue;}
            int r1=Find(X);
            int r2=Find(Y);
            if(r1==r2){
                if((Rank[Y]-Rank[X]+3)%3!=D-1)cnt++;
            }
            else {
                Union(X,Y,D-1);
            }
        }
        printf("%d
    ",cnt);
    }
  • 相关阅读:
    MySQL事务处理2
    servlet过滤器配置白名单、黑名单
    Freemarker生成静态代码实例
    FreeMarker---数据类型
    创建第一个freemarker
    JDBC编程之优化
    关于ComponentName的使用
    launchMode使用详解
    android开发之使用上下文菜单
    android开发之shape详解
  • 原文地址:https://www.cnblogs.com/MalcolmMeng/p/9519871.html
Copyright © 2011-2022 走看看