作为一个学习了蒟蒻我一直觉得匈牙利算法和网络流的二分图匹配速度差不多,虽然要快点但网络流一般也够用了,知道我做了这道题,我发现真的小看了匈牙利。
题目如下:
时间限制:2秒 内存限制:128M
【问题描述】
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
【数据范围】
对于30%的数据,保证N<=1000
对于100%的数据,保证N<=1000000
【来源】
BZOJ 1854
一道很显然的二分图匹配,我也习惯性的打了个网络流。
我们把每个装备往它能表示的点连边,然后每个点往t连边,s往装备连边,流量都是1.
算了一下数据发现必超(最后几个点要8秒多),这时我想起了学了但只打过几次的匈牙利(还是在专门学的时候打的)。
匈牙利算法在这里的优越性在于它相当于只跑了一次网络流,用网络流如果只跑一次差距不会很大,但如果要多次清空跑就完全不如匈牙利了。
代码我就不多说了,打完一看,1秒不到。
代码如下:
网络流算法:(我还秘制优化了的,然并卵)
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int maxn=1010005;
const int inf=200000005;
struct edge
{
int u,v,f,c,next;
}e[4010005*2];
int cnt=-1,cur[maxn],d[maxn],n,s,t,q[maxn*10],f[maxn],a[10005];
void add(int u,int v,int F){
e[++cnt]=(edge){u,v,F,0,f[u]};f[u]=cnt;
e[++cnt]=(edge){v,u,0,0,f[v]};f[v]=cnt;
}
bool bfs(){
memset(d,0,sizeof(d));
int frond=0,root=0;
d[s]=1;
q[frond++]=s;
while(frond!=root)
{
int i=q[root++];
for(int k=f[i];k!=-1;k=e[k].next)
{
int j=e[k].v;
if(d[j]||e[k].f==e[k].c) continue;
d[j]=d[i]+1;
q[frond++]=j;
}
}
return d[t]>0;
}
int dfs(int i,int a){
if(i==t||a==0) return a;
int F,flow=0;
for(int &k=cur[i];k!=-1;k=e[k].next)
{
int j=e[k].v;
if(d[j]==d[i]+1&&(F=dfs(j,min(a,e[k].f-e[k].c)))>0)
{
flow+=F;
a-=F;
e[k].c+=F;
e[k^1].c-=F;
if(a==0) break;
}
}
return flow;
}
int dinic(){
int flow=0;
while(bfs())
{
memcpy(cur,f,sizeof(cur));
flow+=dfs(s,inf);
}
return flow;
}
void init()
{
scanf("%d",&n);
s=0,t=1+n+10000;
int x,y;
for(int i=s;i<=t;i++) f[i]=-1;
for(int i=1;i<=n;i++)
{
add(s,i,1);
scanf("%d%d",&x,&y);
add(i,n+x,1);
add(i,n+y,1);
}
for(int j=1;j<=10000;j++)
{
a[j]=cnt+1;
add(j+n,t,0);
}
}
int main()
{
//freopen("in.txt","r",stdin);
init();
int A=0,B=10000,ans;
while(A<=B)
{
int C=(A+B)>>1;
for(int j=1;j<=C;j++)
e[a[j]].f=1;
for(int j=C+1;j<=10000;j++)
e[a[j]].f=0;
for(int i=0;i<=cnt;i++)
e[i].c=0;
if(dinic()==C) A=C+1,ans=C;
else B=C-1;
}
printf("%d\n",ans);
return 0;
}
匈牙利算法:
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=1010005;
struct edge
{
int v,next;
}e[maxn*10];
int f[10005],mx[maxn],state[maxn],fa[maxn]={0},n,cnt=0,T=0;
void add(int u,int v){
e[++cnt]=(edge){v,f[u]};f[u]=cnt;
}
bool hungary(int x){
for(int k=f[x];k;k=e[k].next)
{
int y=e[k].v;
if(state[y]!=T)
{
state[y]=T;
if(fa[y]==0||hungary(fa[y]))
{
fa[y]=x;
return 1;
}
}
}
return 0;
}
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d",&n);
int x,y;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
add(x,i);add(y,i);
}
for(int i=1;i<=10001;i++)
{
T++;
if(!hungary(i))
break;
}
printf("%d\n",T-1);
return 0;
}