这题可以先构造一个二分图,它的|X|=|Y|=N,每当输入一个数对(x,y)时,连接一条x->y的边,求最小点覆盖即可。
最小点覆盖:对于一个二分图中的每一条边(u,v)都选其中一个顶点(多条边可以选同一点),使得选择的所有点的集合(无重复)元素的数量最小。——我想我已经解释地很清楚了……
最小点覆盖相当于最大匹配(证明见《算法艺术与信息学竞赛》,《算法导论》……),最大匹配我是用网络流(弱智办法)求的,附属代码:
#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;
const int NMax=1500;
struct edge
{
int num,len;
edge *next,*rev;
}*S[NMax],pool[10000];
int N,K,nn,L;
void Build(int x,int y,int z)
{
edge *p=&pool[L++],*q=&pool[L++];
p->num=y; p->len=z; p->next=S[x];
q->num=x; q->len=0; q->next=S[y];
p->rev=q; q->rev=p;
S[x]=p; S[y]=q;
}
int Q[NMax],level[NMax];
bool makelevel()
{
int tmp;
memset(level,-1,sizeof(level));
Q[0]=0;
level[0]=0;
for(int i=0,bot=1;i<bot;i++)
{
tmp=Q[i];
for(edge *p=S[tmp];p;p=p->next)
if(p->len>0 && level[p->num]==-1)
level[Q[bot++]=p->num]=level[tmp]+1;
}
return level[nn]!=-1;
}
int DFS(int a,int alpha)
{
int tot=0,tmp;
if(a==nn) return alpha;
for(edge *p=S[a];p && tot<alpha;p=p->next)
if(p->len>0 && level[p->num]==level[a]+1)
if(tmp=DFS(p->num,min(alpha-tot,p->len)))
{
tot+=tmp;
p->len-=tmp;
if(p->rev!=NULL) p->rev->len+=tmp;
}
if(!tot) level[a]=-1;
return tot;
}
int main()
{
int x,y;
scanf("%d%d",&N,&K);
nn=N*2+1;
for(int i=1;i<=K;i++)
{
scanf("%d%d",&x,&y);
Build(x,N+y,1);
}
for(int i=1;i<=N;i++)
{
Build(0,i,1);
Build(N+i,nn,1);
}
int ans=0,tmp;
while(makelevel())
while(tmp=DFS(0,(~0u>>1)))
ans+=tmp;
printf("%d\n",ans);
return 0;
}