poj2289多重二分匹配

poj2289多重二分匹配

题目部分

题意:
题目说一个人要给电话簿进行分组,每个人可以分到特定的组(一个人只能进一个组,但供选择的组有多个)。要你计算分到人数最多的那个组,最小有几个人

输入数据
多组测试数据,每组测试数据先输入2个数,第一个数是人数n,第二个数是组数m,如果这两个数输入的是0 0表示程序结束。
接下来n行会结束人的信息
名字 可以进入的组(这里没有说明有几组所有输入的时候要做好处理)
这里组是从0开始计算的。

输出数据
最多人数的那个组中最小人数。

样例解析:
3 2
John 0 1
Rose 1
Mary 1
5 4
ACM 1 2 3
ICPC 0 1
Asian 0 2 3
Regional 1 2
ShangHai 0 2
0 0
第一组数据:
有3个人,2个组
John 可以进入 0、1组
Rose 可以进入1组
Mary 可以进入1组
第二组数据:
ACM 可以进入1、2、3组
ICPC 可以进入0、1组
Asian 可以进入0、2、3组
Regional 可以进入1、2组
ShangHai 可以进入0、2组
第一组输出:
可以3个人都进1组,这样最大人数的组的人数是3
可以John进0组,剩下2个人进1组,这样最大人数的组人数是2
所以输出结果是2。(就写一组了,( ⊙ o ⊙ )懒了)

思路部分

要看二分匹配的可以看这个博客:
http://blog.csdn.net/dark_scope/article/details/8880547
(前面一篇博客就推过)
这题跟二分匹配的差别就在于右边可以连接多个左边,所以把二分匹配中保存右边的连接数组改成二维数组就可以了,你可以另外拿一个数组存放了几个数,我这边是用这个二维数组的connect[i][0]来存放第i行存了几个数。
这样要找最小我们就可以遍历所有可能,也就是最大的组的人数可能,从1到n(所有人都进到一个组),这里用二分法搜索加速
像第一组测试数据,假设最少3人进行二分多重匹配,所有人都匹配上那么这种情况是可以的,我们就缩小范围。
2个人是最少的这样所有人还是都匹配上了,继续缩小,1个人最少,这样有人没有匹配上,那么最少的人数就是2了。

代码部分

#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<cstring>
#include<algorithm>
#include<fstream>
#include<math.h>
#include<algorithm>
#include<stack>
#include<queue>
using namespace std;
//fstream fin("1.txt");
//streambuf *buf = cin.rdbuf(fin.rdbuf());//用于重定项输入改成,把cin当成fin
//FILE *buf = freopen("1.txt", "a+", stdin);//用于重定项文件输入,scanf也能用(这里代码的第一行必须加上才能用这句话)

const int inf = 1 << 29;
const int MAXN = 1010;

int n, m;
int limit;
int map[MAXN][MAXN];
int connect[MAXN][MAXN];//每行的0保存这一行存放了几个数
bool vis[MAXN];

bool find(int p)
{
    for (int i = 1; i <= m; i++)
    {
        if (map[p][i] && !vis[i])
        {
            vis[i] = true;
            if (connect[i][0] < limit)
            {
                connect[i][0]++;
                connect[i][connect[i][0]] = p;
                return true;
            }
            for (int j = 1; j <= connect[i][0]; j++)
            {
                if (find(connect[i][j]))
                {
                    connect[i][j] = p;
                    return true;
                }
            }
        }
    }
    return false;
}
bool judge()
{
    memset(connect, 0, sizeof(connect));
    for (int i = 1; i <= n; i++)
    {
        memset(vis, 0, sizeof(vis));
        if (!find(i))//如果返回false,就说明找不到合适的匹配,那么这个Limit就不行直接退出<因为每个人都有被分配到组里面
            return false;
    }
    return true;
}
int main()
{
    char str[20];
    int a;
    int left, right;
    while (cin >> n >> m && (n + m))
    {
        memset(map, 0, sizeof(map));
        for (int i = 1; i <= n; i++)
        {
            scanf_s("%s", str, 20);
            while (true)
            {
                scanf_s("%d", &a);
                map[i][a + 1] = 1;
                if (getchar() == '\n') break;
            }
        }
        left = 0;
        right = n + 1;
        int result;
        while (left <= right)
        {
            limit = (left + right) >> 1;
            if (judge())
            {
                result = limit;
                right = limit - 1;
            }
            else
                left = limit + 1;
        }
        cout << result << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值