题意: 把 N 个人分成 M 组,给出 N 个人各自可选的组别集合,求一种合法的分配方案,使得人数最多的组的人数最少
分析: 最多最少,一般二分跑不了,然后求分配就匈牙利稍微变形就可以了(变形是为了适应二分)
代码:
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
const int N = 1000+10;
vector<int>G[N];
string s;
int n,m;
int match[N][N],cnt[N]; //cnt[i]表示当前第i组已经分配了多少人
//match[i][j]表示第i组第j个人的编号
bool vis[N];
bool dfs(int u,int lim)
{
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(vis[v]) continue;
vis[v]=1;
if(cnt[v]<lim) //还没到上限,可以直接分配
{
match[v][++cnt[v]]=u;
return 1;
}
else
{
for(int k=1;k<=cnt[v];k++) //否则一个一个试
if(dfs(match[v][k],lim))
{
match[v][k]=u;
return 1;
}
}
}
return 0;
}
int KM(int lim)
{
memset(cnt,0,sizeof(cnt));
memset(match,0,sizeof(match));
int res=0;
rep(i,1,n)
{
memset(vis,0,sizeof(vis));
if(dfs(i,lim)) res++;
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
while(cin>>n>>m,n+m)
{
getline(cin,s);
rep(i,1,n) G[i].clear();
rep(i,1,n)
{
getline(cin,s);
int num=0,xb=0;
while(!isdigit(s[xb])) xb++;
while(xb<s.length())
{
if(isdigit(s[xb])) num=num*10+(s[xb]-'0');
else G[i].pb(num),num=0;
xb++;
}
G[i].pb(num);
}
int L=1,R=n,ans;
while(L<=R)
{
int mid=(L+R)>>1;
if(KM(mid)==n) ans=mid,R=mid-1;
else L=mid+1;
}
cout<<ans<<endl;
}
}