这道题是一道二分图多重匹配
题意:
XXX很受欢迎,手机里好友很多,现在想把好友分组,为了好找,每个好友只能加入一个组,并且要让每个组尽量的人数尽量小(某些人可以分到多个组内),问:最多人的组有多少人?
二分匹配是一个对应一个,就好如老师选课问题,一位老师只能教一门课!!
而这道题一位老师可以教n门课,为了不让老师过劳猝死,求每位老师最少教课数
原来的二分匹配已经不行了!!!
新的匹配已经出现
开一个数组 Vlink 表示第i个人进入的分组
再开一个二维数组link表示第i个分组的里面所有好友
新的匹配也很好理解,在原来的基础上只是多加了个link数组
对于每一次的匹配,判断一下是否可以匹配,相当于普通二分匹配的vlink[i]是否为0
然后把这个分组的所有的点都找一遍,看有没有增广路
或者说看看这个人是否可以移到别的分组里面,这样就保证了每个分组都尽量的留有位置
然后下次再匹配到这个组,可以把这个人加进来,如果加不进来,就说明匹配不成功!!
#include<string.h>
#include<stdio.h>
using namespace std;
const int N = 1000+10;
const int M = 500+10;
int n,m;
int e[N][M];
int vlink[M]; //Vlink 其实就是match,但是储存的值为这个点匹配了多少个点(因为一个点可以对应匹配很多点)
int link[M][N]; //此时link就是用来储存m点的所有匹配点
int max_cap;
bool vis[M];
int path(int s)
{
for(int i=0;i<m;i++)
{
if(e[s][i] && !vis[i]) //如果这条路可以通
{
vis[i]=true;
if(vlink[i]<max_cap) //并且i这个点匹配的数量没有达到最大值
{
link[i][vlink[i]++]=s;//那么把这个点放进去
return 1;
}
for(int j=0;j<vlink[i];j++)//对于i匹配到的每一个点
{
if(path(link[i][j]))//如果这点可以移到别的点
{
link[i][j]=s; //那么久进行移动
return 1;
}
}
}
}
return 0;
}
bool max_match(int mid)
{
max_cap=mid;
memset(vlink,0,sizeof(vlink));
for(int i=0;i<n;i++)
{
memset(vis,false,sizeof(vis));
if(!path(i))
return false;
}
return true;
}
int main()
{
char str[20],c;
int a;
while(scanf("%d %d",&n,&m)==2 && (n||m))
{
memset(e,false,sizeof(e));
for(int i=0;i<n;i++) //输入
{
scanf("%s",str);
while(true) //每输入一个数,读取后面的字符,如果是换行符,表示这组数据输入完成
{
scanf("%d%c",&a,&c);
e[i][a]=true;
if(c=='\n') break;
}
}
int left=1,right=n,ans=n;
while(left<=right)
{
int mid=(left+right)>>1;
if(max_match(mid))
{
if(mid<ans)
ans=mid;
right=mid-1;
}
else left=mid+1;
}
printf("%d\n",ans);
}
return 0;
}