题目链接: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;
}