pku2492

唉,这道题更是让我花费了不少时间,好多大牛都说很简单,但是…………

很无语就对了,一下部分接受转自http://www.cppblog.com/abilitytao/archive/2010/05/14/98899.html

大牛虽然解释了好多,但综合了很多的博客里面的介绍之后,我终于有了些许的感悟,我想,结合我在程序中的注释应该会容易理解一点

大牛的介绍:

题目的大意是给出n只bug和m次观察到的性行为,并以此为依据判断两只bugs是不是有同性恋行为(gay)。
比如3只bug
1 2有性行为
2 3有性行为
1 3有性行为
---->>>>>首先1,2是异性。
---->>>>>然后2,3是异性。
可以推出1,3是异性。
但是1,3有性行为,所以可以判断出有一定有同性恋。

剥离这个题目所赋予的外壳,我们抽出这个问题的本质:并查集!
其实,这里最重要的是去维护每一个点到集合顶点的偏移量。(注意:下面生造了一个词 所谓集合元素 比如说f[i]=i,那么i就是集合元素,集合偏移量就是集合元素的偏移量)

初始状态下,应该是
i号点挂在i号集合下面
我们考虑一般情况:假设合并的过程已经进行了一部分 ,这样每一个集合下面都有元素,且各自对于顶点的偏移量都算出来了;
现在在a集合中的元素x和b集合中的元素y进行合并。此时有两中情况改变偏移量;
1.首先是集合的合并,如果要将a,b集合合并,又要保证x,y数字的kind不相同,比如说把b集合挂到a集合下面去。
代表集合的那个元素,他的偏移量永远是0,所以b要改变偏移量,使得b里面的y在进行变换后要和x相异。
如果 kind[x]=0;kind[y]=0;那么y对应的那个代表集合的元素的偏移量必须变成1,因为只有这样才能使得合并后,x,y有不同的kind;
如果 kind[x]=0,kind[y]=1;y对应代表集合的元素偏移量是0,所以对应集合偏移量还是0;
类推   kind[x]=1,kind[y]=0,同上,0;
           kind[x]=1,kind[y]=1,y集合偏移量应该变为1;
综上 可以得到一个同或的关系。
用等式 kind[a]=(kind[x]+kind[y]+1)%2;恰好满足要求.
2.然后是压缩路径时候的偏移量改变
个人认为,这个主要是解决集合合并时候产生的“残余问题”,因为在合并集合的时候只是考虑了集合的偏移量,至于它下面的元素一概不管。一个压缩路径既分离了父子元素的偏移量,又使得子元素直接指向集合元素。

#include<stdio.h>
#define MAXN 2010
int f[MAXN],kind[MAXN],n;
int find(int x)
{
	if(x==f[x])
		return x;
	int t=find(f[x]);
	kind[x]=(kind[x]+kind[f[x]])%2;
	//Union函数中仅对集合的偏移量做出了修改,并未对集合中元素的偏移量修改,注意里面的递归调用,可以发现:
	//括号中的kind[x]表示的是与父节点的关系,等式左边的那个表示与跟节点的关系,而kind[f[x]]表示的是与根节点的关系
	//所以根据kind[x]和kind[f[x]]则可以推出x与根节点的关系
	f[x]=t;
	return f[x];
}
int Union(int x,int y)
{
	int a=find(x);
	int b=find(y);
	if(a==b)//表明x与y已经建立了联系
	{
	if(kind[x]==kind[y])
		return 1;//表示有同性恋
	}
	else{
	f[a]=b;//将x所在的a集合并入y所在的b集合
	kind[a]=(kind[x]+kind[y]+1)%2;//这里研究了好久,x跟a的关系确定,y跟b的关系确定,从而推出a跟b的关系
	}
	return 0;
}
int main()
{
	int m,i,j=0,flag,t,a,b;
	scanf("%d",&t);
	while(t--)
	{
		flag=0;
		scanf("%d %d",&n,&m);
		for(i=1;i<=n;i++)
		{
			f[i]=i;
			kind[i]=0;
		}
		for(i=1;i<=m;i++)
		{
			scanf("%d %d",&a,&b);
			if(Union(a,b))
				flag=1;
		}
		  if(flag==1)
            printf("Scenario #%d:\nSuspicious bugs found!\n\n",++j);
        else 
            printf("Scenario #%d:\nNo suspicious bugs found!\n\n",++j);
	}
	return 0;
}


总而言之,并查集的操作就是不断地维护者各个集合中,每个元素身上对集合元素的偏移关系。从而确定他们是否具有同性恋。
在这个题中,假设是不存在同性恋的,所以只有找到矛盾才输出 有同性恋。

原文地址:https://www.cnblogs.com/nanke/p/2035396.html