题目描述
lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。游戏进行到最后,lxhgww遇到了终极boss,这个终极boss很奇怪,攻击他的装备所使用的属性值必须从1开始连续递增地攻击,才能对boss产生伤害。也就是说一开始的时候,lxhgww只能使用某个属性值为1的装备攻击boss,然后只能使用某个属性值为2的装备攻击boss,然后只能使用某个属性值为3的装备攻击boss……以此类推。现在lxhgww想知道他最多能连续攻击boss多少次?

输入格式
输入的第一行是一个整数N,表示lxhgww拥有N种装备接下来N行,是对这N种装备的描述,每行2个数字,表示第i种装备的2个属性值

输出格式
输出一行,包括1个数字,表示lxhgww最多能连续攻击的次数。

输入输出样例

输入
3
1 2
3 2
4 5
输出
2

说明/提示
Limitation

对于30%的数据,保证N < =1000

对于100%的数据,保证N < =1000000


这道题一般有两种解法(二分图,并查集),本博客采用二分图来写,效率挺高的。

为什么可以用二分图呢?或者说怎么用二分图写。

物品具有两个属性,相当于对于一个物品的两个属性,最多被使用一次,那我们怎么限制这种条件呢?

首先我们一般会想到拆点(二分图常用技巧),但是这道题拆点也不能限制,于是我们不难想到对于每个物品,我们新加一个点,然后把属性的两个点与之相连,这样跑二分图匹配时,每个属性点就只会被匹配一次了。

这道题还要求是连续的攻击,怎么解决呢?我们可以每次用属性点去求与之对应的匹配,如(1, 2,3, 4)依次去找增广路径,只要有一个不能找到了,我们就退出循环。(采用匈牙利的性质,故此题不能跑最大流)

每次都清空vis太慢了,所以我们采用时间戳 id 来解决。

AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,M=10010;
int n,res,mat[N<<1],vis[N<<1];
int head[N<<1],nex[N<<2],to[N<<2],tot;
void add(int a,int b){
	to[++tot]=b; nex[tot]=head[a]; head[a]=tot;
}
bool find(int x,int id){
	for(register int i=head[x];i;i=nex[i]){
		if(vis[to[i]]!=id){
			vis[to[i]]=id;//时间戳id
			if(!mat[to[i]]||find(mat[to[i]],id)){
				mat[to[i]]=x;	return 1;
			}
		}
	}
	return 0;
}
signed main(){
	scanf("%lld",&n);
	for(register int i=1;i<=n;i++){
		int a,b;	scanf("%lld %lld",&a,&b);	add(a,i+M);	add(b,i+M);
	}
	for(register int i=1;i<=10000;i++){
		if(find(i,i))	res++;
		else	break;
	}
	printf("%lld\n",res);
	return 0;
}