[SCOI2010]连续攻击游戏
题目描述
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;
}