BZOJ1854 [Scoi2010]游戏(并查集/二分图匹配)

一题多解,挺不错的题


解法一:并查集

【题解】

将一个装备抽象成一条边,它连接着编号为其属性值的两个结点 
这样,取装备等价于取边; 每个装备只能用一次,等价于在每条边上仅能取一个端点 
因此,连好所有的边,构成一个个连通块,它们产生了这样一条性质:
对于某一连通块,若其为一棵树,则它的所有结点(属性值)中,只有一个不能取。(因为树的边比点少一,每条边上只能取一个点)
                            若其含有环,则它的所有结点(属性值)都能取。(边数于节点数相等,每条边上只能取一个点)
那么,我们只需从小到大枚举每个属性值,若它所在的块为树,且它的值是树中结点的最大值,它就是第一个不能取的值 


【代码】

#include<stdio.h>
#include<stdlib.h>
int hash[10005],fa[1000005],max[1000005],hascir[1000005];
int father(int x)
{
	if(fa[x]!=x) fa[x]=father(fa[x]);
	return fa[x];
}
int main()
{
	int n,i,x,y;
	scanf("%d",&n);
	for(i=1;i<=10000;i++)//注意:不是从1到n循环(卡片属性可以超过n)
		fa[i]=i;
	for(i=1;i<=n;i++)
	{
		scanf("%d%d",&x,&y);
		hash[x]=hash[y]=1;
		if(father(x)==father(y)) hascir[fa[x]]=1;
		if(max[fa[x]]<x) max[fa[x]]=x;
		if(max[fa[x]]<y) max[fa[x]]=y;
		fa[fa[y]]=fa[x];
	}
	for(i=1;i<=n;i++)//答案不会超过n
	{
		x=father(i);
		if(hash[i]==0||(max[x]==i&&hascir[x]==0)) break;
	}
	printf("%d",i-1);
	return 0;
}


解法二:二分图匹配

【题解】

所有属性值为x集合,所有装备为y集合 
将y中的每个装备t与其在x集合中的属性值s1,s2连边 

因为每个装备只允许用一次,所以y中的一个元素它只能与x中的一个元素匹配 
这是一个二分图匹配问题 

给x中的结点(代表元素值)从小到大连匹配边,哪个结点找不到增广路,就代表这个属性值无法在比它小的值都出现的情况下通过装备体现出来,答案就得到了 


【代码】

#include<stdio.h>
#include<stdlib.h>
int v[2000005],first[10005],next[2000005],left[2000005],y[2000005];
int e=0,ans;
void tj(int x,int y)
{
	v[++e]=y;
	next[e]=first[x];
	first[x]=e;
}
int find(int x)
{
	int i;
	for(i=first[x];i!=0;i=next[i])
		if(y[v[i]]<ans)
		{
			y[v[i]]=ans;
			if(left[v[i]]==0|find(left[v[i]])==1)
			{
				left[v[i]]=x;
				return 1;
			}
		}
	return 0;
}
int main()
{
	int n,i,x,y;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d%d",&x,&y);
		tj(x,i);
		tj(y,i);
	}
	for(ans=1;ans<=n;ans++)
		if(find(ans)==0) break;
	printf("%d",ans-1);
	return 0;
}


你可能感兴趣的:(树,环,并查集,二分图,一题多解)