zoukankan      html  css  js  c++  java
  • P5836 [USACO19DEC]Milk Visits S 从并查集到LCA(最近公共祖先) Tarjan算法 (初级)

    为什么以它为例,因为这个最水,LCA唯一黄题。

    首先做两道并查集的练习(估计已经忘光了)。简单来说并查集就是认爸爸找爸爸的算法。先根据线索理认爸爸,然后查询阶段如果发现他们的爸爸相同,那就是联通一家的,不同就不是一家的。

    两道简单例题

    P1551 亲戚       P1536  村村通

    以P1551 亲戚为例

    题目背景

    若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。

    题目描述

    规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。

    输入格式

    第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。

    以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Mi和Mj具有亲戚关系。

    接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。

    输出格式

    P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系

     代码核心部分find函数:

    int find(int num)
    {
        if(die[num]==num)
        {
            return num;
        }
            die[num]= find(die[num]);//记忆化,缩减递归产生的重复,找过的就记下来。
        return die[num];
    }

    找到自己的爸爸的爸爸的爸爸……,然后找到根后用die[]记录下来。

    完整代码

    #include<cstdio>
    #include<iostream>
    using namespace std;
    int n,m,p,c,d,die[5050];
    int find(int num)
    {
        if(die[num]==num)//发现他自己主宰自己,直接返回他自己(递归的出口)
        {
            return num;
        }
        die[num]= find(die[num]);//如果不是,递归找他爸爸的爸爸
        return die[num];
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&p);
        for(int i=1;i<=n;i++)
        {
            die[i]=i;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&c,&d);
            die[find(c)]=find(d);//找到c的爸爸的爸爸,然后让c的爸爸认d的爸爸为爸爸……  当然可以c和d反过来存。
        }
    
        for(int i=1;i<=p;i++)
        {
            scanf("%d%d",&c,&d);
            if(find(c)==find(d))//查询一下他们的爸爸是不是同一人
            {
                printf("Yes
    ");
            }
            else
            {
                printf("No
    ");
            }
        }        
    } 

    P1536 村村通

    题目描述

    某市调查城镇交通状况,得到现有城镇道路统计表。表中列出了每条道路直接连通的城镇。市政府 "村村通工程" 的目标是使全市任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要相互之间可达即可)。请你计算出最少还需要建设多少条道路?

    输入格式

    输入包含若干组测试测试数据,每组测试数据的第一行给出两个用空格隔开的正整数,分别是城镇数目 nn 和道路数目 mm ;随后的 mm 行对应 mm 条道路,每行给出一对用空格隔开的正整数,分别是该条道路直接相连的两个城镇的编号。简单起见,城镇从 11 到 nn 编号。

    注意:两个城市间可以有多条道路相通。

    输出格式

    对于每组数据,对应一行一个整数。表示最少还需要建设的道路数目。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    int n,m,die[1000],c,d,e,f,ans;
    int find(int num)
    {
        if (die[num]==num)return num;
        return  die[num]=find(die[num]);//还是记忆化,注意养成递归思考避免重复运算的习惯
    }
    int main()
    {
        while(1)
        {
            scanf("%d",&c);
            if(c==0)break;
            for(int i=1;i<=c;i++)die[i]=i;
            ans=0;
            scanf("%d",&d);
            for(int i=0;i<d;i++)
            {
                scanf("%d%d",&e,&f);
                die[find(e)]=find(f);
            }
            for(int i=1;i<=c;i++)
            {
                if(die[i]==i)ans++;//统计多少自己管理自己的孤儿,每个孤儿需要一条道路
            }
            if(ans==0){
                printf("%d
    ",0);
            }
            else printf("%d
    ",ans-1);    
        }
        
    }

    题目大致就是寻找有多少孤苦伶仃的孤儿,统计一下输出。和亲戚一题差不太多。

    分割一下————————————————

    以下是利用并查集理论寻找最近公共祖先。这种算法叫做Tarjan算法(读作塔珍,译作塔扬,是发明大佬的名字)

    这只是简化版的裸LCA题,改天奉上复杂版的。

    P5836 [USACO19DEC]Milk Visits S

    题目描述请传送看洛谷。

    题目的大体思路就是将有连接的同类奶牛用并查集的方式聚集起来,他们有共同的祖先。

    然后查询是否起点和终点有共同的祖先,如果有,而且不是这个人爱喝的那种牛奶,那这个人很遗憾喝不到自己想要的牛奶,输出0

    如果起点终点不在一个联通块或者起点就是他想喝的奶,那么他一定开心 输出1

    #include<cstdio>
    #include<iostream>
    using namespace std;
    int n,m,a,b,die[100010],ans;
    char c,se[100010];    
    int find(int num)
    {
        if(die[num]==num)
        {
            return num;
        }
        die[num]=find(die[num]);
        return die[num];    
    }
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            die[i]=i;
            cin>>se[i];
        }
        for(int i=1;i<n;i++)
        {
            scanf("%d %d",&a,&b);
    
            if(se[a]==se[b])
            {
                die[find(a)]=find(b);//颜色相同还联通,集合起来。
            }
        }
        for(int i=1;i<=m;i++)
        {    
            scanf("%d%d",&a,&b);
            scanf(" %c",&c);//这里特别注意%c之前的空格,可以抵消掉输入时候的空格,否则c就存个空格
            if(se[a]!=c)//如果起步就不是他喜欢的牛奶
            {
                if(find(a)==find(b))//而且落脚和起步在一个联通块,别想了,一路上没有什么变化,都是不想喝的牛奶
                {
                    printf("0");
                }
                else
                {
                    printf("1");//起步和落脚不在一个联通块,说明有不同的牛奶,一定开心!
                }
            }
            else
            {
                printf("1");
            }
        }
    } 

    抛砖引玉,LCA入门题。一同学习,一同加油!

    题目描述

    Farmer John 计划建造 NN 个农场,用 N-1N1 条道路连接,构成一棵树(也就是说,所有农场之间都互相可以到达,并且没有环)。每个农场有一头奶牛,品种为更赛牛或荷斯坦牛之一。

    Farmer John 的 MM 个朋友经常前来拜访他。在朋友 ii 拜访之时,Farmer John 会与他的朋友沿着从农场 A_iAi 到农场 B_iBi 之间的唯一路径行走(可能有 A_i = B_iAi=Bi)。除此之外,他们还可以品尝他们经过的路径上任意一头奶牛的牛奶。由于 Farmer John 的朋友们大多数也是农场主,他们对牛奶有着极强的偏好。他的有些朋友只喝更赛牛的牛奶,其余的只喝荷斯坦牛的牛奶。任何 Farmer John 的朋友只有在他们访问时能喝到他们偏好的牛奶才会高兴。

    请求出每个朋友在拜访过后是否会高兴。

    输入格式

    输入的第一行包含两个整数 NN 和 MM

    第二行包含一个长为 NN 的字符串。如果第 ii 个农场中的奶牛是更赛牛,则字符串中第 ii 个字符为 G,如果第 ii 个农场中的奶牛是荷斯坦牛则为 H

    以下 N-1N1 行,每行包含两个不同的整数 XX 和 YY1 leq X, Y leq N1X,YN),表示农场 XX 与 YY 之间有一条道路。

    以下 MM 行,每行包含整数 A_iAiB_iBi,以及一个字符 C_iCiA_iAi 和 B_iBi 表示朋友 ii 拜访时行走的路径的端点,C_iCi 是 G 或 H 之一,表示第 ii 个朋友喜欢更赛牛的牛奶或是荷斯坦牛的牛奶。

    输出格式

    输出一个长为 MM 的二进制字符串。如果第 ii 个朋友会感到高兴,则字符串的第 ii 个字符为 1,否则为 0

  • 相关阅读:
    E百科 | 第2期 扒一扒能加速互联网的QUIC协议
    阿里的 RocketMQ 如何让双十一峰值之下 0 故障?
    阿里巴巴开源容器镜像加速技术
    Fluid — 云原生环境下的高效“数据物流系统”
    一站式云原生智能告警运维平台——SLS新版告警发布!
    重磅官宣:Nacos2.0 发布,性能提升 10 倍
    阿里云高级技术专家周哲:阿里云边缘云原生应用实践
    互联网泛娱乐直播安全的解决之道
    货运物流移动端解决方案:为货运物流行业打造高性能、高粘性的“双端”触点
    SpringMVC(十四) RequestMapping ModelAndView
  • 原文地址:https://www.cnblogs.com/stdcpsx/p/13188989.html
Copyright © 2011-2022 走看看