Description
Input
Output
Sample Input
3 2 John 0 1 Rose 1 Mary 1 5 4 ACM 1 2 3 ICPC 0 1 Asian 0 2 3 Regional 1 2 ShangHai 0 2 0 0
Sample Output
2 2
//
/*
杰米有一个电话薄,现在她要把电话薄的n个联系人分成m个组,现在给出
每个联系人对应的姓名以及该联系人可以分到的组的序号。现在让你确定
一种符合杰米分配方案,使该方案中的组中的联系人个数的最大值最小
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<map>
using namespace std;
const int N=4010;
const int M=5500000;//M不能太小 否则RE
const int inf=(1<<28);
int head[N];
struct Edge
{
int u,v,next,w;
} edge[M];
int cnt,n,s,t;//n从0开始 0->n-1
void addedge(int u,int v,int w)
{
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].v=u;
edge[cnt].u=v;
edge[cnt].w=0;
edge[cnt].next=head[v];
head[v]=cnt++;
}
int sap()
{
int pre[N],cur[N],dis[N],gap[N];
int flow=0,aug=inf,u;
bool flag;
for(int i=0; i<n; i++)
{
cur[i]=head[i];
gap[i]=dis[i]=0;
}
gap[s]=n;
u=pre[s]=s;
while(dis[s]<n)
{
flag=0;
for(int &j=cur[u]; j!=-1; j=edge[j].next)
{
int v=edge[j].v;
if(edge[j].w>0&&dis[u]==dis[v]+1)
{
flag=1;
if(edge[j].w<aug) aug=edge[j].w;
pre[v]=u;
u=v;
if(u==t)
{
flow+=aug;
while(u!=s)
{
u=pre[u];
edge[cur[u]].w-=aug;
edge[cur[u]^1].w+=aug;
}
aug=inf;
}
break;
}
}
if(flag) continue;
int mindis=n;
for(int j=head[u]; j!=-1; j=edge[j].next)
{
int v=edge[j].v;
if(edge[j].w>0&&dis[v]<mindis)
{
mindis=dis[v];
cur[u]=j;
}
}
if((--gap[dis[u]])==0)
break;
gap[dis[u]=mindis+1]++;
u=pre[u];
}
return flow;
}
//初始化 cnt=0;memset(head,-1,sizeof(head));
char str[N];
int main()
{
int m,p;
while(scanf("%d%d",&m,&p)==2&&m)
{
cnt=0;
memset(head,-1,sizeof(head));
n=m+p+2;
s=0,t=n-1;
for(int i=1;i<=m;i++)
{
addedge(0,i,1);
scanf("%s",str);
while(1)
{
int x;scanf("%d",&x);
addedge(i,x+1+m,1);
char ch=getchar();
if(ch=='\n') break;
}
}
for(int i=1;i<=p;i++) addedge(m+i,t,1);
int l=0,r=2000;
while(l<r)
{
int mid=(l+r)>>1;
for(int i=0;i<cnt;i++)
{
if(i&1) edge[i].w=0;
else if(edge[i].u<=m) edge[i].w=1;
else edge[i].w=mid;
}
int tmp=sap();
if(tmp==m) r=mid;
else l=mid+1;
}
printf("%d\n",r);
}
return 0;
}