题意
给定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;
}