题目链接
http://poj.org/problem?id=2289
题意
杰米是一个非常受欢迎的女孩,拥有不少朋友,所以她总是在她的手机中保留一个很长的联系人列表。联系人列表变得如此之长以致于她通常需要很长时间才能浏览整个列表以找到朋友的号码。作为杰米最好的朋友和编程天才,你建议她将联系人列表分组,并且最大限度地减少最大组的人数,这样她可以更容易地在组中搜索朋友的号码。杰米接受你的建议,并给你她的整个联系人列表,其中包含她朋友的姓名,她希望拥有的组数以及每个朋友可能属于的组的编号。
解题
二分枚举每个分组的最大容量x,将其变成一个判定性问题:各个组的容量都为x下能否将这些联系人成功分组。
而该判定性问题就是一个明显的二分图多重匹配问题,如果存在完备匹配(即匹配数等于左侧点数),则该判定性问题能解。
AC代码
//750ms 3316kB
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <map>
using namespace std;
const int maxn=1e3+100;
const int maxm=550;
int from[maxm][maxn];
int cap[maxm],num[maxm];
int g[maxn][maxm],vis[maxm];
int n,m;
bool Find(int u)
{
for(int v=0;v<m;v++)
{
if(!g[u][v] || vis[v]) continue;
vis[v]=1;
if(cap[v]>num[v])
{
num[v]++;
from[v][num[v]]=u;
return true;
}
for(int i=1;i<=num[v];i++)
if(Find(from[v][i]))
{
from[v][i]=u;
return true;
}
}
return false;
}
int hungary()
{
memset(num,0,sizeof(num));
int ans=0;
for(int i=0;i<n;i++)
{
memset(vis,0,sizeof(vis));
if(Find(i)) ans++;
}
return ans;
}
bool judge(int x)
{
for(int i=0;i<m;i++) cap[i]=x;
return hungary()==n;
}
void solve()
{
int l=0,r=1000;
while(l<r)
{
int mid=(l+r)>>1;
if(judge(mid))
r=mid;
else
l=mid+1;
}
printf("%d\n",r);
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
if(n==0 && m==0) break;
memset(g,0,sizeof(g));
char str[20];
for(int i=0;i<n;i++)
{
scanf("%s",str);
while(getchar()!='\n')
{
int x;
scanf("%d",&x);
g[i][x]=1;
}
}
solve();
}
return 0;
}