【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),输出假话的总数。 

Input

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

Output

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

Sample Input

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

Sample Output

3

 

解题报告:

其中,在判断真假话之后如果是真话就必须更新这句真话。

其实判断此类并查集问题的关键是先搞清楚这两个节点之间的关系。

比如 1 2 3这个操作,如果要你判断2和3是同类这一个命题。

那么,我们就找2和3相对于祖宗的性质,比如2被其祖宗吃,3吃其祖宗,那么如果2和3是同一个祖宗(同一个集合)显然这个命题不成立。

如果2和3不是同一个祖宗,那么我们现在就需要合并两个祖宗,因此打表一下就能得出两个祖宗之间的关系。

2的操作也是这个原理。

 

贴一份测试数据:POJ - 食物链 - 测试数据

 

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>using namespace std;
const int MAX=50000 + 5;
int f[MAX];
int rank[MAX];
int n,k,ans; 
int getf(int x) {if(x!=f[x]) {int tmp=getf(f[x]);rank[x]=(rank[x]+rank[f[x] ] ) % 3;f[x]=tmp;}return f[x];
}
bool merge(int d,int x,int y) {int t1=getf(x);int t2=getf(y);if(t1==t2) {if((rank[y]-rank[x]+3)%3!=d)return 1;else return 0;}else {f[t2]=t1;rank[t2]=(rank[x]-rank[y]+d+3) % 3;return 0;}}
int main()
{int d,u,v;scanf("%d%d",&n,&k);ans=0;//初始化 for(int i=1;i<=n;i++) {f[i]=i;rank[i]=0;}while(k--) {scanf("%d %d %d",&d,&u,&v);if(u>n||v>n||(u==v&&d==2) ) {ans++;continue;}if(merge(d-1,u,v) ) ans++;}printf("%d\n",ans);return 0;
}

 

附一个博客的讲解:https://blog.csdn.net/c0de4fun/article/details/7318642

Part I  - 权值(relation)的确定。
    我们根据题意,森林中有3种动物。A吃B,B吃C,C吃A。
    我们还要使用并查集,那么,我们就以动物之间的关系来作为并查集每个节点的
    权值。
    注意,我们不知道所给的动物(题目说了,输入只给编号)所属的种类。
    所以,我们可以用动物之间“相对”的关系来确定一个并查集。
    0 - 这个节点与它的父节点是同类
    1 - 这个节点被它的父节点吃
    2 - 这个节点吃它的父节点。
    注意,这个0,1,2所代表的意义不是随便制定的,我们看题目中的要求。
    说话的时候,第一个数字(下文中,设为d)指定了后面两种动物的关系:
    1 - X与Y同类
    2 - X吃Y
    我们注意到,当 d = 1的时候,( d - 1 ) = 0,也就是我们制定的意义
                当 d = 2的时候,( d - 1 ) = 1,代表Y被X吃,也是我们指定的意义。
    所以,这个0,1,2不是随便选的
    Part II - 路径压缩,以及节点间关系确定
    确定了权值之后,我们要确定有关的操作。
    我们把所有的动物全初始化。
    struct Animal
    {
        int num; //该节点(node)的编号
        int parent; //该node的父亲
        int relation; //该node与父节点的关系,0同类,1被父节点吃,2吃父节点
    }; Animal ani[50010];
        初始化为
        For i = 0 to N do
            ani[i].num = i;
            ani[i].parent = i;
            ani[i].relation = 0 ; //自己和自己是同类
        End For
        (1)路径压缩时的节点算法
        我们设A,B,C动物集合如下:(为了以后便于举例)
        A = { 1 , 2 , 3 ,4 ,5 }
        B = { 6 , 7 , 8 ,9 ,10}
        C = { 11, 12, 13,14,15}
        假如我们已经有了一个集合,分别有3个元素
        SET1 = {1,2},我们规定集合中第一个元素为并查集的“代表”
        假如现在有语句:
        2 2 6
        这是一句真话
        2是6的父亲
         ani[6].parent = 2;
         ani[6].relation = 1;
        那么,6和1的关系如何呢?
         ani[2].parent = 1;
         ani[2].relation = 0;
        我们可以发现6与2的关系是 1.
        通过穷举我们可以发现
        ani[now].parent = ani[ani[now].parent].parent;
        ani[now].relation = ( ani[now].relation + ani[now.parent].relation ) % 3;
        这个路径压缩算法是正确的
        关于这个路径压缩算法,还有一点需要注意的地方,我们一会再谈
        注意,根据当前节点的relation和当前节点父节点的relation推出
        当前节点与其父节点的父节点的relation这个公式十分重要!!
        它推不出来下面都理解不了!!自己用穷举法推一下:
        好吧,为了方便伸手党,我给出穷举过程
                i      j
        爷爷  父亲  儿子  儿子与爷爷
               0      0       (i + j)%3 = 0
               0      1       (i + j)%3 = 1
               0      2       (i + j)%3 = 2
               1      0       (i + j)%3 = 1
               1      1       (i + j)%3 = 2
               1      2       (i + j)%3 = 0
               2      0       (i + j)%3 = 2
               2      1       (i + j)%3 = 0
               2      2       (i + j)%3 = 1
        嗯,这样可以看到,( 儿子relation + 父亲relation ) % 3 = 儿子对爷爷的relation
        这就是路径压缩的节点算法
        (2) 集合间关系的确定
        在初始化的时候,我们看到,每个集合都是一个元素,就是他本身。
        这时候,每个集合都是自洽的(集合中每个元素都不违反题目的规定)
        注意,我们使用并查集的目的就是尽量的把路径压缩,使之高度尽量矮
        假设我们已经有一个集合
        set1 = {1,2,7,10}
        set2 = {11,4,8,13},每个编号所属的物种见上文
        set3 = {12,5,4,9}
        现在有一句话
        2 13 2
        这是一句真话,X = 13,Y = 2
        我们要把这两个集合合并成一个集合。
        直接
        int a = findParent(ani[X]);
        int b = findParent(ani[Y]);
        ani[b].parent = a;
        就是把Y所在集合的根节点的父亲设置成X所在集合的根节点。
        但是,但是!!!!
        Y所在集合的根结点与X所在集合的根节点的关系!!!要怎么确定呢?
        我们设X,Y集合都是路径压缩过的,高度只有2层
        我们先给出计算的公式
        ani[b].relation = ( 3 - ani[Y].relation + ( d - 1 ) + ani[X].relation) % 3;
        这个公式,是分三部分,这么推出来的
        第一部分,好理解的一部分:
        ( d - 1 ) :这是X和Y之间的relation,X是Y的父节点时,Y的relation就是这个
        3 - ani[Y].relation = 根据Y与根节点的关系,逆推根节点与Y的关系
        这部分也是穷举法推出来的,我们举例:
        j
        子         父相对于子的relation(即假如子是父的父节点,那么父的relation应该是什么,因为父现在是根节点,所以父.relation = 0,我们只能根据父的子节点反推子跟父节点的关系)
         0             ( 3 - 0 ) % 3 = 0
         1(父吃子)   ( 3 - 1 ) % 3 = 2 //父吃子
         2(子吃父)    ( 3 - 2 ) % 3 = 1 //子吃父,一样的哦亲
        ——————————————————————————————————————————————————————
        我们的过程是这样的:
        把ani[Y],先连接到ani[X]上,再把ani[Y]的根节点移动到ani[X]上,最后,把ani[Y]的根节点移动到ani[X]的根节点上,这样算relation的
        还记得么,如果我们有一个集合,压缩路径的时候父子关系是这么确定的
        ani[爷爷].relation = ( ani[父亲].relation + ani[儿子].relation ) % 3
        我们已知道,( d - 1 )就是X与Y的relation了
        而 (3 - ani[Y].relation)就是 以Y为根节点时,他的父亲的relation
        那么
        我们假设把Y接到X上,也就说,现在X是Y的父亲,Y原来的根节点现在是Y的儿子
          Y的relation   +     ani[Y]根节点相对于ani[Y]的relation
        ( ( d - 1 )         +    ( 3 - ani[Y].relation) ) % 3
        就是ani[Y]的父亲节点与ani[X]的relation了!
        那么,不难得到,ani[Y]的根节点与ani[X]根节点的关系是:
        ( ( d - 1 ) + ( 3 - ani[Y].relation) + ani[X].relation ) % 3 ->应用了同余定理
        注意,这个当所有集合都是初始化状态的时候也适用哦
        还是以最开头我们给的三个集合(分别代表三个物种)为例
        2 1 6
        带入公式
        ani[6].relation = ( ( 2 - 1 ) + ( 3 - 0 ) + 0 ) % 3 = 1
        也就是,6被1吃
    Part III - 算法正确性的证明
        首先,两个自洽的集合,合并以后仍然是自洽的
        这个不难想吧,数学上有个什么对称性定理跟他很像的。
        如果理解不了,就这么想!!
        当set1和set2合并之后,set2的根节点得到了自己关于set1根节点的
        正确relation值,变成了set1根节点的儿子,那么
        set2的所有儿子只要用
        ( ani[X].relation + ani[Y].relation ) % 3就能得到自己正确的relation值了
        所以说,针对不在同一集合的两个元素的话,除非违背了(2)和(3),否则永远是真的
        (无论这句话说的是什么,我们都可以根据所给X,Y推出两个子节点之间应有的关系,这个关系一确定,所有儿子的关系都可以确定)
        其实所有的不同集合到最后都会被合并成一个集合的。
        我们只要在一个集合中找那些假话就可以了。
        首先,如何判断
        1 X Y是不是假话。//此时 d = 1
        if ( X 和 Y 不在同一集合)
            Union(x,y,xroot,yroot,d)
        else
            if x.relation != y.relation  ->假话
        其次,如何判断
        2 X Y是不是假话 //此时d = 2
        if ( X 和 Y 不在同一集合)
            Union(x,y,xroot,yroot,d)
        else
            (ani[y].relation + 3 - ani[x].relation ) % 3 != 1 ->假话
        这个公式是这么来的:
        3 - ani[x].relation得到了根节点关于x的relation
        ani[y] + 3 - ani[x].relation得到了y关于x的relation
        所以,只要y关于x的relation不是1,就是y不被x吃的话,这句话肯定是假话!
        (2)路径压缩要特别注意的一点(错在这里,要检讨自己)
            路径压缩的时候,记得要
            先findParent,再给当前节点的relation赋值。
            否则有可能因为当前节点的父节点的relation不正确而导致错的稀里哗啦。
            例子:
            set1 = {1,2,7,10}
            set2 = {3,4,8,11}
            set3 = {12,5,14,9}
            Union(1,3,1,3,1)
            Union(3,12,3,12,2)
            1 5 1
            算5的relation
            如果不先更新parent的relation,算出来应该是
            ( 3 - 0 + 0 + 1 ) % 3 = 1,5被1吃,显然不对
            这里面,+ 0的那个0是指根节点 12 的relation(未更新,这里的0是指12与11的relation)
            如果更新完了的话,应该是
            ( 3 - 0 + 2 + 1 ) % 3 = 0 ,5与1是同一物种,对了
            这里面的 2 是更新节点12的relation(12与1的relation)


#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>using namespace std;
const int c0de4fun = 50010;//动物个数的最大值
///指明父节点与自己的关系,0同类,1被吃,2吃父
const int SAME = 0;
const int ENEMY = 1;
const int FOOD = 2;
struct Animal
{int parent;int num;int relation;
};
Animal ani[c0de4fun];
long ans;
int findParent(Animal* node)
{///Wrong Answer 因为这个函数写错了///这个函数得是“自洽的”///就是说,得保证每个元素的父亲的relation是对的///再算自己的relation///因为自己的relation和父亲的relation有关///这就是为什么要先findParent再relation更新的原因int tmp;if( node->parent == node->num )return node->parent;tmp = node->parent;
#ifdef DBGprintf("Animal %d s Parent is %d\n",node->num,node->parent);
#endif// node->relation = ( ani[node->parent].relation + node->relation ) % 3;node->parent = findParent(&ani[node->parent]);node->relation = ( ani[tmp].relation + node->relation ) % 3;return node->parent;
}
void Union(int x,int y,int a,int b,int d)
{ani[b].parent = a;///rootY.parent = rootX.parent;ani[b].relation =( (3 - ani[y].relation) + (d - 1) + ani[x].relation) % 3;
}void init_Animal(int n)
{for(int i = 1 ; i <= n ; i++){ani[i].num = i;ani[i].parent = i;ani[i].relation = SAME;}
}
int main(int argc,char* argv[])
{int N,K;int d,X,Y;
#ifdef INPUTfreopen("b:\\acm\\poj1182\\input.txt","r",stdin);
#endifscanf("%d%d",&N,&K);init_Animal(N);for(int i = 0 ; i < K ; i++){scanf("%d%d%d",&d,&X,&Y);if( X > N || Y > N)ans++;else{if(d == 2 && X == Y)ans++;else{int a = findParent(&ani[X]);int b = findParent(&ani[Y]);if ( a != b ){///x,y不在同一集合中Union(X,Y,a,b,d);}else{switch(d){case 1:if(ani[X].relation != ani[Y].relation)ans++;break;case 2:if(((ani[Y].relation + 3 - ani[X].relation) % 3 ) != 1)ans++;break;}}}}}printf("%d\n",ans);return 0;
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/442074.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

腐蚀单机怎么进_暖气片堵塞是什么原因?要怎么解决呢?

你知道散热器到底为什么堵塞吗&#xff1f;散热器堵塞了怎么办&#xff1f;下面和金旗舰散热器小编一起来看看吧~一、散热器堵塞怎么办首先&#xff0c;把进回水阀先全部关闭&#xff0c;用扳手将散热器的堵头轻轻拧开。这里需要注意的是&#xff0c;堵头对应的散热器下面要放一…

layui弹出界面空白页问题

弹出界面时&#xff0c;有时会出现空白界面&#xff0c;应该如何处理&#xff1f; 1 尝试解决方式&#xff1a;在open方法的success回调方法中&#xff0c;获取当前iframe高度&#xff0c;重新赋予新的高度&#xff1b; let ifr layero.find(iframe)[0]; let bHeight ifr.s…

vspy如何在图形面板显示报文_设备实时状态监控:如何进行工业生产设备数据采集?...

设备实时状态监控&#xff1a;如何进行工业生产设备数据采集&#xff1f;数据采集(DAQ)&#xff0c;是指从传感器和其它待测设备等模拟和数字被测单元中自动采集非电量或者电量信号,送到上位机中进行分析&#xff0c;处理。慧都设备数据采集系统解决方案工业生产设备数据采集是…

【POJ - 2236】Wireless Network (并查集)

题干&#xff1a; An earthquake takes place in Southeast Asia. The ACM (Asia Cooperated Medical team) have set up a wireless network with the lap computers, but an unexpected aftershock attacked, all computers in the network were all broken. The computers …

如何使用微信公众平台测试号进行系统开发

申请一个测试号&#xff1a;入口修改测试公众号自定义菜单&#xff08;使用微信公众平台接口调试工具&#xff09;网站开发&#xff0c;进行部署网站测试

【POJ - 1751】Highways (最小生成树)

题干&#xff1a; The island nation of Flatopia is perfectly flat. Unfortunately, Flatopia has a very poor system of public highways. The Flatopian government is aware of this problem and has already constructed a number of highways connecting some of the …

jupyter怎么安装jieba_AI工具:Anaconda中Jupyter不能import已安装module问题解决

jupyter模式下写代码时,通过pip install package命令行安装package完成之后,无法在jupyter模式下import &#xff0c;这是个通用的问题&#xff0c;我这里遇到的是import jieba&#xff0c;可能import 别的package也会出现&#xff0c;记录下&#xff0c;也花了点时间排查。。。…

Sql Server数据库设置一个账户只能看到一个数据库

1 新建登录名&#xff0c;注意不要设置用户映射&#xff0c;服务器角色只选择public&#xff08;默认必选&#xff0c;无法去掉&#xff0c;可以添加其他服务器角色&#xff0c;但是不要添加查看所有数据库的权限&#xff0c;接下来会去掉public的查看所有数据库权限&#xff0…

boot lib分离 spring_spring boot + gradle打包bootJar分离lib

以前项目打包一直是用的maven&#xff0c;最近新开一个项目&#xff0c;使用的是spring boot 2.11 gradle 4.10.3&#xff0c;在打包的时候分离lib折腾了好几天&#xff0c;网上找了很多方法都不成功&#xff0c;老是卡在configurations.compile这里&#xff0c;总是获取不到正…

【POJ - 3494】Largest Submatrix of All 1’s(加一点思维后化成 单调栈)

题干&#xff1a; Given a m-by-n (0,1)-matrix, of all its submatrices of all 1’s which is the largest? By largest we mean that the submatrix has the most elements. Input The input contains multiple test cases. Each test case begins with m and n (1 ≤ m…

如何在修改计算机设置时,不再弹出提示框?

1 打开控制面板&#xff0c;找到安全与维护&#xff1b; 2 进入安全与维护&#xff0c;在安全中找到“用户账户控制UAC”&#xff0c;点击打开“更改设置” 3 进行设置&#xff0c;“从不通知”设置之后&#xff0c;针对计算机的所有更改将不再提示

doe五步法_DOE方法介绍

DOE, Design of Experiment。就是试验设计。想知道对于某个过程的产生影响的诸多因素中对输出结果影响最关键的因素有哪些&#xff0c;就可以用DOE方法来设计一系列试验&#xff0c;获知关键影响因素。通常的确定对输出变量最关键影响因子的试验做法有三种。1. 经验猜测法。首先…

【POJ - 1789】【ZOJ - 2158】【SCU - 1832】Truck History (最小生成树)

题干&#xff1a; Description Advanced Cargo Movement, Ltd. uses trucks of different types. Some trucks are used for vegetable delivery, other for furniture, or for bricks. The company has its own code describing each type of a truck. The code is simply a…

matlab两张图片合成一张_11. 图像合成与图像融合

本文同步发表在我的微信公众号“计算摄影学”&#xff0c;欢迎扫码关注【转载请注明来源和作者】我们终于进入了新的篇章。这一次我来给大家介绍一下图像合成与融合。我们经常看到一些很奇妙的PS技术&#xff0c;例如下面这张&#xff0c;它把1928年的一位叫做Frankie Yale的黑…

C#学习,Web界面打开winform程序

1 Web打开winform程序&#xff0c;需要使用的标签为a标签 <a href"SelfName://">连接提示</a> //SelfName 是自己的自定义协议2 Winform程序一般不需要做任何操作&#xff0c;如果需要参数传递的情况下&#xff0c;可以将Winform的Program.cs程序修…

【POJ - 2349】【UVA - 10369】 Arctic Network(最小生成树求权值第k大的边)(内附两种算法)

题干&#xff1a; The Department of National Defence (DND) wishes to connect several northern outposts by a wireless network. Two different communication technologies are to be used in establishing the network: every outpost will have a radio transceiver a…

asp.net MVC结合Blazor开发学习

1建立MVC项目&#xff08;.net 6&#xff09;; 2 在项目启动文件Program.cs中添加Blazor框架&#xff1b; var builder WebApplication.CreateBuilder(args);// Add services to the container. builder.Services.AddControllersWithViews(); builder.Services.AddServerSid…

澄海口袋机器人_汕头市澄海区在2019年汕头市中小学智能机器人竞赛上取得优异成绩...

4月14日&#xff0c;2019年汕头市中小学智能机器人竞赛在汕头市蓬鸥中学举行。本次活动由汕头市教育局和汕头市科协联合主办&#xff0c;全市93所学校的249个队伍503名选手报名参赛&#xff0c;大赛设“高铁时代”机器人现场拼装赛、丛林任务挑战赛、超级轨迹赛、综合技能比赛、…

【POJ - 1703】Find them, Catch them(带权并查集之--种类并查集 权为与父节点关系)

题干&#xff1a; Find them, Catch them Time Limit: 1000MS Memory Limit: 10000KTotal Submissions: 36176 Accepted: 11090 Description The police office in Tadu City decides to say ends to the chaos, as launch actions to root up the TWO gangs in the city, Ga…

h5前端如何实现F11全屏功能

网站全部实现小demo&#xff1a;兼容谷歌和Edge {Layout null; }<!DOCTYPE html><html> <head><meta name"viewport" content"widthdevice-width" /><title>Index</title><script src"~/Content/jquery/jque…