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