您的位置:首页 > 其它

POJ 1703 Find them, Catch them【典型并查集:判断在不同的集合】

2013-04-08 19:04 531 查看
Findthem,Catchthem

TimeLimit:1000MSMemoryLimit:10000K
TotalSubmissions:23915Accepted:7167
Description

ThepoliceofficeinTaduCitydecidestosayendstothechaos,aslaunchactionstorootuptheTWOgangsinthecity,GangDragonandGangSnake.However,thepolicefirstneedstoidentifywhichgangacriminalbelongsto.Thepresentquestionis,given
twocriminals;dotheybelongtoasameclan?Youmustgiveyourjudgmentbasedonincompleteinformation.(Sincethegangstersarealwaysactingsecretly.)

AssumeN(N<=10^5)criminalsarecurrentlyinTaduCity,numberedfrom1toN.Andofcourse,atleastoneofthembelongstoGangDragon,andthesameforGangSnake.YouwillbegivenM(M<=10^5)messagesinsequence,whichareinthefollowingtwokinds:

1.D[a]

where[a]and[b]arethenumbersoftwocriminals,andtheybelongtodifferentgangs.

2.A[a][b]

where[a]and[b]arethenumbersoftwocriminals.Thisrequiresyoutodecidewhetheraandbbelongtoasamegang.

Input

ThefirstlineoftheinputcontainsasingleintegerT(1<=T<=20),thenumberoftestcases.ThenTcasesfollow.EachtestcasebeginswithalinewithtwointegersNandM,followedbyMlineseachcontainingonemessageasdescribedabove.
Output

Foreachmessage"A[a][b]"ineachcase,yourprogramshouldgivethejudgmentbasedontheinformationgotbefore.Theanswersmightbeoneof"Inthesamegang.","Indifferentgangs."and"Notsureyet."
SampleInput

1
55
A12
D12
A12
D24
A14

SampleOutput

Notsureyet.
Indifferentgangs.
Inthesamegang.

Source

POJMonthly--2004.07.18

[b]题意
:有两个不同的帮派,每个帮派至少有一个人。判断两个人是否属于同一个帮派。
有T组测试数据。
给你N个人,编号从1到N,操作M次。
每次操作输入一个字符和两个数x,y
如果字符为A则判断x和y是否属于同一个帮派,并且输出结果。
如果字符为D则明确告诉你x和y是属于不同帮派的。

算法:经典并查集的应用。
PS:以前接触的并查集都是让我们判断是否属于同一个连通分量,但这道题却让你判断是否属于同一类。
开始小纠结了下,如果我昨天没有纠结清楚POJ1182食物链那题想必肯定是做不出来的,其实想清楚了,这道题就是食物链
那题的简单版本。


思路:除了像普通的并查集定义一个p[]记录父亲节点外,还定义一个r[]记录当前点与其所属的连通分量的根节点的关系。
r[]=0表示属于同一个帮派;r[]=1表示与其根节点属于不同的帮派。

开始时初始化自己是自己的父亲p[x]=x,自己与自己属于同一类r[x]=0.
一旦输入D断定x和y属于不同集合后,就连接x和y所在的树,同时更新r[]
一旦输入A
如果find(x)不等于find(y)说明还没有判断过x与y直接输出关系不确定即可
Notsureyet.

如果find(x)等于find(y),但是他们的r不等,说明属于不同帮派,输出Indifferentgangs.

如果他们的r相等,说明属于同一个帮派,则输出Inthesamegang


注意:1.find()函数寻找根节点的时候要不断的更新r

根据子节点与父亲节点的关系和父节点与爷爷节点的关系,推导子节点与爷爷节点的关系

如果a和b的关系是r1,b和c的关系是r2,

那么a和c的关系就是(r1+r2)%2.PS:因为只用两种情况所以对2取模。

如果实在不好理解,那么我们就枚举推理一下,共有2*2=4种情况:

(a,b)(b,c)(a,c)(r1+r2)%2

0	000a和b是同类,b和c是同类,所以a和c也是同类

0111a和b是同类,b和c是异类,所以a和c也是异类

1011a和b是异类,b和c是同类,所以a和c是异类

1100a和b是异类,b和c是异类,所以a和c是同类

2.Union()联合两棵树的时候也要更新两棵树的根的关系

定义:fx为x的根节点,fy为y的根节点

联合时,使得p[fx]=fy;同时也要寻找fx与fy的关系。关系为:(r[x]+r[y]+1)%2

如何证明?

fx与x的关系是r[x],

x与y的关系是1(因为确定是不同类,才联合的),

y与fy关系是r[y],模2是因为只有两种关系

所以又上面的一点所推出的定理可以证明fx与fy的关系是:(r[x]+r[y]+1)%2



/*
D
Accepted
916KB
329ms
C++
1120B
2013-04-0818:29:33
*/
#include<cstdio>

constintmaxn=100000+10;

intp[maxn];//存父亲节点
intr[maxn];//存与根节点的关系,0代表同类,1代表不同类

intfind(intx)//找根节点
{
if(x==p[x])returnx;

intt=p[x];//记录父亲节点方便下面更新r[]
p[x]=find(p[x]);
r[x]=(r[x]+r[t])%2;//根据子节点与父亲节点的关系和父节点与爷爷节点的关系,推导子节点与爷爷节点的关系
returnp[x];//容易忘记
}

voidUnion(intx,inty)
{
intfx=find(x);//x所在集合的根节点
intfy=find(y);

p[fx]=fy;//合并
r[fx]=(r[x]+1+r[y])%2;//fx与x关系+x与y的关系+y与fy的关系=fx与fy的关系
}
voidset(intn)
{
for(intx=1;x<=n;x++)
{
p[x]=x;//自己是自己的父节点
r[x]=0;//自己和自己属于同一类
}
}

intmain()
{
intT;
intn,m;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%*c",&n,&m);
set(n);

charc;
intx,y;
while(m--)
{
scanf("%c%d%d%*c",&c,&x,&y);//注意输入
//printf("%c\n",c);
if(c=='A')
{
if(find(x)==find(y))//如果根节点相同,则表示能判断关系
{
if(r[x]!=r[y])printf("Indifferentgangs.\n");
elseprintf("Inthesamegang.\n");
}
elseprintf("Notsureyet.\n");
}
elseif(c=='D')
{
Union(x,y);
}
}
}
return0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: