题目来源:http://poj.org/problem?id=2289
题意
电话薄利有太多人,分块整理下,已知每个人分在各自的可能的所有块里,问,使得块里的人数的最大值最小是多少。
思路
很显然,这是一道二分多重匹配问题,与二分匹配问题不同的是,用于存储匹配信息的一位数组变成了二维数组。
整个模拟思想与二分图最大匹配一般无二,只是,写法有点异同,这里采用的是二分枚举上限(最小值(最终结果)),然后取增广路。
等学了最大流再重新做这道题。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000+10;
int mp[maxn][maxn],group[maxn][maxn];
int vis[maxn];
int n,m,limit;
void init()
{
char s[20];
memset(mp,0,sizeof(mp));
for(int i=1; i<=n; i++)
{
scanf("%s",s);
do
{
int j;
scanf("%d",&j);
mp[i][j+1]=1;
}
while(getchar()!='\n');
}
}
bool dfs(int i)
{
for(int j=1; j<=m; j++)
{
if(mp[i][j]&&!vis[j])
{
vis[j]=1;
if(group[j][0]<limit)
{
++group[j][0];
group[j][group[j][0]]=i;
return 1;
}
for(int k=1;k<=group[j][0];k++)
{
if(dfs(group[j][k]))
{
group[j][k]=i;
return 1;
}
}
}
}
return 0;
}
bool deal()
{
memset(group,0,sizeof(group));
for(int i=1; i<=n; i++)
{
memset(vis,0,sizeof(vis));
if(!dfs(i))
{
return 0;
}
}
return 1;
}
void solve()
{
int left=1,right=n,res;
while(left<=right)//二分的作用让我晕了半天。。。就是枚举limit而已
{
limit=(left+right)>>1;
if(deal())
{
res=limit;
right=limit-1;
}
else
left=limit+1;
}
printf("%d\n",res);
}
int main()
{
while(~scanf("%d%d",&n,&m)&&(n||m))
{
init();
solve();
}
}