[SCOI2010]连续攻击游戏(二分图/并查集)

题意

给定n个物品,每个物品有两个属性(a,b)可选择,对每一个物品确定一个属性,使得1~k这些属性同时存在且k最大

思路

1. 二分图匹配

两个取能且仅能取一个,容易想到二分图,但是如果直接用a向b连边并不能解决问题,所以用a,b向编号i连边。WHY?这个亚子连边可以表示:①i只有一个属性,②一个属性a只需要和一个物品连边

然后for i 1~n 拿属性去一 一匹配物品,当不能匹配就结束循环

2. 并查集

将a直接向b连边,这样做会出现一些连通块,对于每一个连通块,如果它是一颗树,那么一定存在一种方法取点,使得只有一个点没有被取到;如果它不是一颗树,那么一定存在一个环,这种情况下所有点都能被取到

用并查集维护连通性,同时记录连通块是否有环、连通块大小

for i 1~n,如果i在环中则一定可以取,如果不在,但是连通块还有其它的点,优先选取i点,否则i+1之后的免谈\(......\)

Code:

#include<bits/stdc++.h>
#define N 1000005
using namespace std;
int n,t,size,maxx;
int fa[N],num[N];
bool cir[N];
template <class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
}
int find(int x) {return x==fa[x] ? x : fa[x]=find(fa[x]);}
int main()
{
	read(n);
	for(int i=1;i<=n+1;++i) fa[i]=i,num[i]=1;
	for(int i=1;i<=n;++i)
	{
		int x,y;
		read(x);read(y);
		int fx=find(x),fy=find(y);
		if(fx==fy) cir[fx]=1;
		else
		{
			cir[fx]=(cir[fx]|cir[fy]);
			cir[fy]=0;
			num[fx]+=num[fy];
			fa[fy]=fx;
		}
	}
	int i;
	for(i=1;i<=n+1;++i)
	{
		int fi=find(i);
		if(!cir[fi])//没有环才可能取不到
		{
			if(num[fi]==1) break;//没有边了,取不到
			--num[fi]; 
		}
	}
	cout<<i-1<<endl;
	return 0;
}
posted @ 2019-07-26 21:21  擅长平地摔的艾拉酱  阅读(193)  评论(0编辑  收藏  举报
/*取消选中*/