这道题目,想了好久,也是刚刚开始做并查集专题。对模型还不是太熟悉,总的来说,一般的并查集题目有三个步骤是不能缺少的,1,构建,2,查找,3,合并。
第一步也就是把每个节点都当做单个的集合处理,第二步就是找根节点(但是期间的方式有很多种),第三步,就是合并两个集合,即把一个连通分量中的根节点,连接到另一个去,使之成为一棵树。
这只是一个基本的模型,也就是说,你做并查集的题目在头脑中至少应该有的框架,而且并查集的题目我觉得基本不能让你暴力过。
这个题目和这样普通的模型不同,比如杭电的小希的迷宫还有畅通工程。普通的并查集题目,元素只有两种可能,第一在这个集合,第二不再,但是这个题目多出一种不确定的选项。所以,模型需要改变。
题目大意;
两个帮派,有N个罪犯,给你m条信息,D a b,说明编号为a b的两个人不再一个帮派,A a b 表示让你判断是否在一个帮派,输出有三种情况。个人认为,把两个帮派的人的不同的关系理解成敌对和盟友比较好理解(别急,之后你就知道这样的好处了,赶紧ORZ自己一下)。首先 D a b表示两个人肯定是有联系的,因为两个人是敌是友都要靠彼此的条件来制约,我们把这样有关系的并在一起,作为一个集合。那么在这个集合中的人,是一定有关系的,但是可能是敌,也可能是友,如果你对这个事实有怀疑的话,就找简单的1---4号数据试试,你会突然发现一个显而易见的事实(尼玛,我就是在这里想到了正确思路,结果没想完全,又改了),敌人的敌人是朋友啊,有木有啊有木有各位,知道什么意思了吧,也知道刚才我为什么让大家理解成朋友和敌人的关系了吧。显然,当罪犯一多的时候我们就会发现,再一个已经建好的一个集合里面,任意两个节点的关系式由他们分别到根节点关系的距离决定的,无论他们到根节点的距离是基数还是偶数,只要是相等,那么他们肯定是朋友,否则则为敌人。那么,我们就要为每一个定点附加一个信息,即每个定点到根节点的距离是多少。
接下来就是本题目的重点了,如何计算距离。合并和找父节点都是easy。
第一:在合并的时候更新距离
我们在把两个集合合并的时候,通常会采用合并连个集合的根节点的办法,那么一开始两个根节点的距离应该都是0,因为他们本身就是根节点。当开始合并的时候,要连接上去的那个根节点的距离就要+1,因为它从根节点降了一级,有了根节点之后,它的距离自然不是0了。当然,每次求距离的时候都要进行%2处理,因为如果是奇数,那么肯定和根节点是敌对关系,如果是偶数,肯定是朋友关系,你要是问为什么,就一句话,“敌人的敌人是朋友”,要是还不理解我也就没办法了。需要特别注意的是,此处更新 距离值并不需要更新要连接的那个集合根节点以下的节点到新的根节点的距离值,这个更新会在另外一个函数中进行
第二,在查找的时候更新距离
题目与普通并查集题目很大的不同就是,这个题目在查找的时候要压缩路径。至于距离的更新,他是用如下的方式
首先,如果一开始 1 2 为一个集合,3 4为一个集合。两个集合连接在一起之后,1为根节点。那么四个顶点的距离依次是 0 1 1 1.此时如果我想找2 4的关系,那么很显然,4的距离显然不是之前的数值了,需要更新,所以,在递归根节点之后,每退回的下一个节点,都要更新这个节点到根节点的距离,那么3显然距离还是为1,但是4的距离等于。3到根节点的距离+4到3的距离,结果更新成功,变为2,再%2变为0.
这样的读者就可以看出,如果一个节点的父节点,也就是根节点不变的话,那么他所具有的距离就没变,一旦根节点(父节点)变了,那么就要立刻更新。
前面的,都是在一个集合里,已经确定集合内的点有关系的情况,那么没有给出信息,也就是没有连接在一起的点,你不能确定他们是敌人还是朋友,因为题目中说,所给的信息也是不完全的。那么思路出来了,我们就来看代码吧。
代码如下
#include<stdio.h>
#include<string.h>
#define MAX 100005
int set[MAX];
int pos[MAX];
int n,m;
void creat(int n)
{
int i;
for(i=1;i<=n;i++)
{
set[i]=i; //初始化每个点为单个集合//
pos[i]=0; //他们到各自根节点的距离初始化为0//
}
}
int find(int x)
{
int i,r;
if(x==set[x])
return x;
else
{
r=find(set[x]);
pos[x]=(pos[x]+pos[set[x]])%2;
}
return set[x] = r;
}
int bing(int a,int b)
{
int fx,fy;
fx=find(a);
fy=find(b);
if(fx==fy)
return 1;
else
set[fx]=fy;
pos[fx]=(pos[a]+pos[b]+1)%2;
return 1;
}
int main()
{
int T;
int a,b;
char flag;
int i,j,k;
while(scanf("%d",&T)!=EOF)
{
while(T--)
{
scanf("%d%d",&n,&m);
creat(n);
while(m--)
{
getchar();
scanf("%c%d%d",&flag,&a,&b);
if(flag=='A')
{
if(find(a)!=find(b))
printf("Not sure yet.\n");
else
{
if(pos[a]==pos[b])
printf("In the same gang.\n");
else
printf("In different gangs.\n");
}
}
else
bing(a,b);
}
}
}
return 0;
}