Back to Underworld(带权并查集)

A - Back to Underworld
Crawling in process... Crawling failed Time Limit:4000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu
Submit Status Practice LightOJ 1009

Description

The Vampires and Lykans are fighting each other to death. The war has become so fierce that, none knows who will win. The humans want to know who will survive finally. But humans are afraid of going to the battlefield.

So, they made a plan. They collected the information from the newspapers of Vampires and Lykans. They found the information about all the dual fights. Dual fight means a fight between a Lykan and a Vampire. They know the name of the dual fighters, but don't know which one of them is a Vampire or a Lykan.

So, the humans listed all the rivals. They want to find the maximum possible number of Vampires or Lykans.

Input

Input starts with an integer T (≤ 10), denoting the number of test cases.

Each case contains an integer n (1 ≤ n ≤ 105), denoting the number of dual fights. Each of the next n lines will contain two different integers u v (1 ≤ u, v ≤ 20000) denoting there was a fight between u and v. No rival will be reported more than once.

Output

For each case, print the case number and the maximum possible members of any race.

Sample Input

2

2

1 2

2 3

3

1 2

2 3

4 2

Sample Output

Case 1: 2

Case 2: 3

     题意:

     有T(1到10)个case,每个case开头包含一个N(1到100000),说明给出N条信息。每条信息包含A和B,说明A和B属于不同一队的人,至于是哪一队的人是不确定的,根据给出的所有信息,求出人数最大值

的一队人数。

    

     思路:

     首先要弄清楚这个最大值指的是什么。

     比如给出

     1 2

     2 3

     那么很自然的就知道1和3是一个队的,2是一个队的,那么最大人数就是2;

     还比如给出

     1 2

     2 3

     4 5

     5 6

     5 7

     那么可以归纳出,1和3一队,2一队;4,6,7一队,5一队;这时候一共有四队人数出来,但是总共只能有两队的人数,题意要求使人数最大,那么将1,3,4,6,7归结为一队,2和5归结为一队,那么最大人数就为5了。

     所以,这道题给出的信息并一定确保只有两个集合的,所以不能单纯的只用同不同一个根节点来判断人数大小,一开始理解的时候就发现了这个问题。

     可以用带权并查集来做,每给出一条信息就代表A和B是不同队的人,那么用0表示同一队,用1表示不同队。互相有关联的人会结在同一棵树上,(比如上面给出的例子二,1,2,3会在一颗树上,4,6,7,5会在另外一颗树上)一棵树上包含有两队人,通过判断子节点与根节点R的0,1关系来判断是敌人还是朋友,统计子节点与根节点R是1的数量(也就是敌人的数量)保存于nfri数组中,同时合并的时候用num数组来记录每个根节点一下包含一共有多少个子节点。那么这个根节点R下的敌人数为fri[R],队友数为num[R]-fri[R]。要使人数最多,那么遍历所有root[R]==R时候的根节点,ans+=max(fri[R],num[R]-fri[R])。求出的ans即为所求。

 

    AC:

 

#include<stdio.h>
#define max 20000+5
int root[max],num[max],re[max],nfri[max],vis[max];
//root记录根节点,num记录总数,re记录0,1关系
//nfri记录敌人数,vis记录这个人是否出现过
int find(int a)
{
	if(root[a]==a) return a;
	int r=find(root[a]);
	re[a]=(re[a]+re[root[a]])%2;
	root[a]=r;
	return r;
}

int main()
{
	int t;
	scanf("%d",&t);
	for(int i=1;i<=t;i++)
	{
		int n,ans=0;
		scanf("%d",&n);
		for(int j=1;j<max;j++)
		{  
		  root[j]=j;
		  num[j]=1;
		  re[j]=0;
		  nfri[j]=0;
		  vis[j]=0;
	        }
		while(n--)
		{
			int a,b;
			int fa,fb;
			scanf("%d%d",&a,&b);
			fa=find(a);
			fb=find(b);
			if(fa!=fb)
			{
				root[fa]=fb;
				num[fb]+=num[fa];
//统计该父节点下的总人数
				re[fa]=(re[b]+1-re[a])%2;
			}
			vis[a]=1;
			vis[b]=1;
		}
		
		for(int j=1;j<max;j++)
		{
			if(vis[j])
//如果这个人出现过
			{
				int fj=find(j);
//找到这个人的父节点
//因为find函数已经路径压缩了,这时候j直接指向fj
//那么re[j]就已经是代表j和fj的关系了
				if(re[j]) nfri[fj]++;
//如果re[j]==1,说明它与根节点为敌人关系,所以这个根节点的敌人人数增加1
			}
		}
//统计敌人数
		for(int j=1;j<max;j++)
		{
			if(vis[j]&&j==root[j])
			{
				ans+=((nfri[j]>num[j]-nfri[j])?nfri[j]:num[j]-nfri[j]);
			}
		}
//求最大值
		printf("Case %d: %d\n",i,ans);
	}
	return 0;
}

    总结:

    也想到有多集合的情况,但是没想到要按和最大来匹配人数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值