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