• 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;
    }
    


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

  • 相关阅读:
    数据库内外连接以及自然连接
    Mybatis的一级二级缓存
    彻底弄懂CAS单点登录
    Tomcat部署项目的方式
    redis集群脑裂以及解决方案
    AOP分析--代理方式的选择
    线程池
    数据结构--结构体
    Python程序--选择判断
    C语言--密码问题
  • 原文地址:https://www.cnblogs.com/nanke/p/2035396.html
Copyright © 2020-2023  润新知