题目链接:http://poj.org/problem?id=2289
Jamie is a very popular girl and has quite a lot of friends, so she always keeps a very long contact list in her cell phone. The contact list has become so long that it often takes a long time for her to browse through the whole list to find a friend's number. As Jamie's best friend and a programming genius, you suggest that she group the contact list and minimize the size of the largest group, so that it will be easier for her to search for a friend's number among the groups. Jamie takes your advice and gives you her entire contact list containing her friends' names, the number of groups she wishes to have and what groups every friend could belong to. Your task is to write a program that takes the list and organizes it into groups such that each friend appears in only one of those groups and the size of the largest group is minimized.
Input
There will be at most 20 test cases. Ease case starts with a line containing two integers N and M. where N is the length of the contact list and M is the number of groups. N lines then follow. Each line contains a friend's name and the groups the friend could belong to. You can assume N is no more than 1000 and M is no more than 500. The names will contain alphabet letters only and will be no longer than 15 characters. No two friends have the same name. The group label is an integer between 0 and M - 1. After the last test case, there is a single line `0 0' that terminates the input.
Output
For each test case, output a line containing a single integer, the size of the largest contact group.
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
题目翻译
杰米是一个很受欢迎的女孩,有很多朋友,所以她总是在她的手机里留一个很长的联系人名单。联系人列表已变得很长,以至于她通常需要很长时间才能浏览整个列表才能找到朋友的电话号码。作为 Jamie 最好的朋友和编程天才,您建议她将联系人列表分组,并最小化最大组的大小,以便她更容易在群组中搜索好友的号码。Jamie 接受您的建议,并为您提供她的整个联系人列表,其中包含她朋友的姓名、她希望拥有的团体数量以及每个朋友可以属于哪些组。您的任务是编写一个程序,将列表组织成组,以便每个好友只出现在其中一个组中,并且最大组的大小最小化。
输入
最多有 20 个测试用例。缓动大小写从包含两个整数 N 和 M 的行开始,其中 N 是联系人列表的长度,M 是组数。然后,N 行跟随。每行包含好友的姓名以及好友可能所属的组。可以假定 N 不超过 1000,M 不超过 500。名称将仅包含字母表字母,并且不会超过 15 个字符。没有两个朋友有同一个名字。组标签是介于 0 和 M - 1 之间的整数。在最后一个测试用例之后,有一行"0 0"终止输入。
输出
对于每个测试用例,输出包含单个整数的行,最大接触组的大小。
经典的二分图多重最大匹配题,其实多重最大匹配还是很好理解的,就是给Y点集匹配的match数组从一维变成二维,另一维的限制就是容量。对于这个题来说,我们可以去二分枚举那个最大组的大小,然后把带入最大匹配的模板,看能否符合题意,如果符合,看能不能缩小范围,否则就扩大范围。而且这个要注意读入数据时很恶心。
引的别人的一段话:
在原图上建立源点S和汇点T,S向每个X方点连一条容量为该X方点L值的边,每个Y方点向T连一条容量为该Y方点L值的边,原来二分图中各边在新的网络中仍存在,容量为1(若该边可以使用多次则容量大于1),求该网络的最大流,就是该二分图多重最大匹配的值。
#include <iostream>
#include<cstring>
using namespace std;
int n,m,visited[1005],match[1005][1005],G[1005][1005],cnt[1005];
int find(int x,int limit){
for(int i = 1;i<=m;++i){
if(!visited[i]&&G[x][i]){
visited[i]=1;
if(cnt[i]<limit){
match[i][cnt[i]++]=x;
return 1;
}
else{
for(int j = 0;j<cnt[i];++j){
if(find(match[i][j],limit)){
match[i][j]=x;
return 1;
}
}
}
}
}
return 0;
}
int judge(int limit)
{
memset(cnt,0,sizeof(cnt));
int ans=0;
for(int i = 1;i<=n;++i){
memset(visited,0,sizeof(visited));
if(!find(i,limit)) return 0;
}
return 1;
}
int main(int argc, char** argv) {
while(~scanf("%d%d",&n,&m)){
if(!n&&!m) break;
memset(G,0,sizeof(G));
for(int i = 1;i<=n;++i){
char str[10];
scanf("%s",str);
while(1){
char ch;
int x;
scanf("%d%c",&x,&ch);
G[i][x+1]=1;
if(ch=='\n') break;
}
}
int l=1,r=n;
while(l<r){
int mid=(r-l)/2+l;
if(judge(mid)) r=mid;
else l=mid+1;
}
printf("%d\n",r);
}
return 0;
}