题目链接:http://poj.org/problem?id=2289
题目大意:杰米有n个联系人(名字不会重复),现在每个联系人都有一些可能的分组,现在她要把所有的联系人把分组。要求最多人的分组人数最少。
思路:先二分一下这个最小值mid,然后设置右边的点容量为mid。直接跑二分图多重匹配。如果==n。说明可行。
/*
匈牙利算法解决多重匹配问题
*/
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <cstdio>
#include <vector>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1e3+5;//左边最大点数
const int maxm=5e2+5;//右边最大点数
int graph[maxn][maxm],vis[maxm];//图G和增广路访问标记
int match[maxm][maxn];//左边元素与右边元素第n次匹配
int nx,ny;//左边点数,右边点数,边数
int vol[maxm];//右边点多重匹配可容纳值
int cnt[maxm];//右边点已匹配的点数
bool find_path(int u){//找增广路
for(int i=1; i<=ny; i++){//注意,这里节点是从1开始编号
if(graph[u][i] && !vis[i]){//不在增广路
vis[i]=1;//放进增广路
if(cnt[i]<vol[i]){//如果当前已匹配数量小于可容纳量,则直接匹配
match[i][cnt[i]++]=u; return true;
}
for(int j=0; j<cnt[i]; j++){
if(find_path(match[i][j])){//如果先前已匹配右边的点能另外找到增广路,则此点仍可匹配
match[i][j]=u; return true;
}
}
}
}
return false;
}
int max_match()//计算多重匹配的最大匹配数
{
int res=0;
memset(match,-1,sizeof(match));
memset(cnt,0,sizeof(cnt));
for(int i=1; i<=nx; i++){//注意,理由同上!!
memset(vis,0,sizeof(vis));
if(find_path(i)) res++;
}
return res;
}
bool all_match(){//判断左边的点是否都与右边的点匹配了
memset(match,-1,sizeof(match));
memset(cnt,0,sizeof(cnt));
for(int i=0; i<nx; i++){
memset(vis,0,sizeof(vis));
if(!find_path(i)) return false;
}
return true;
}
bool inline read(int &x){
x = 0;
char c = getchar();
while(c>'9'||c<'0') c = getchar();
while(c>='0'&&c<='9'){
x = x*10+c-'0';
c = getchar();
}
if(c=='\n')
return false;
return true;
}
int main(){
int n, m;
while(scanf("%d%d", &n, &m), (n&&m)){
memset(graph, 0, sizeof(graph));
nx=n; ny=m;
for(int u = 1; u <= n; u++){
int v;
while(read(v)){
graph[u][v+1]=1;
}
graph[u][v+1]=1;
}
int l=1, r=n, k;
while(l<=r){
int mid=l+r>>1;
for(int i=1; i<=ny; i++){
vol[i]=mid;
}
if(max_match()==n){
r=mid-1;
k=mid;
}
else{
l=mid+1;
}
}
cout<<k<<endl;
}
return 0;
}
京公网安备 11010502036488号