POJ -2289 -Jamie's Contact Groups (二分图多重匹配)

这道题是一道二分图多重匹配


题意:

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;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值