這題可以先構造一個二分圖,它的|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;
}