hdu 1669(二分图多重匹配+二分枚举)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1669

题意:在通讯录中有N个人,每个人能可能属于多个group,现要将这些人分组m组,设各组中的最大人数为max,求出该最小的最大值

解题思路:解决这道题之前,首先要搞清楚二分图的多重匹配问题。

在二分图最大匹配中,每个点最多只能够和一条匹配边相关联,然而我们经常会遇到这样的问题,即二分图匹配中一个点可以和多条匹配边相关联,但有上限,或者说,n表示点i最多可以和多少个匹配边相关联。

解决方法:改进匈牙利算法

1、从G={X,Y;E}中取一个初始匹配值M,设置上限下限。

2、对最大限制n进行二分匹配。

3、若x都被M匹配,则可行,转至2。否则若与yi的匹配数量小于n,则匹配xi,yi

4、如果yi匹配达到上限,那么在yi中选择一个元素增广,如果能够让出位置,匹配xi,yi,转至2


这里完全就是一个模板了,先用二分枚举最大人数,然后用在用匈牙利算法判断是否满足即可。

#include
#include
#include
using namespace std;

const int maxn = 1005;
int n,m,max_cap,g[maxn][maxn];
int link[maxn][maxn],num[maxn];
bool vis[maxn];
char str[maxn];

bool dfs(int u)
{
	for(int i = 0; i < m; i++)
	{
		if(g[u][i] > 0 && vis[i] == false)
		{
			vis[i] = true;
			if(num[i] < max_cap)
			{
				link[i][num[i]++] = u;
				return true;
			}
			for(int j = 0; j < num[i]; j++)
			{
				if(dfs(link[i][j]))
				{
					link[i][j] = u;
					return true;
				}
			}
		}
	}
	return false;
}

bool Max_Match(int limit)
{
	max_cap = limit;
	memset(link,0,sizeof(link));
	memset(num,0,sizeof(num));
	for(int i = 0; i < n; i++)
	{
		memset(vis,false,sizeof(vis));
		if(!dfs(i)) return false;
	}
	return true;
}

int getDigit(int s,int e)
{
	int ans = 0;
	for(int i = s; i <= e; i++)
		ans = ans * 10 + str[i] - '0';
	return ans;
}

int main()
{
	while(cin >> n >> m,n+m)
	{
		memset(g,0,sizeof(g));
		for(int i = 0; i < n; i++)
		{
			cin >> str;
			gets(str);
			int len = strlen(str);
			for(int j = 1,k; j < len; j = k + 1)
			{
				k = j;
				while(str[k] != ' ' && str[k] != '\0') k++;
				g[i][getDigit(j,k-1)] = 1;
			}
		}
		int l = 1,r = n,mid,ans;
		while(l <= r)
		{
			mid = (l + r) >> 1;
			if(Max_Match(mid))
			{
				ans = mid;
				r = mid - 1;
			}
			else l = mid + 1;
		}
		printf("%d\n",ans);
	}
	return 0;
}


你可能感兴趣的:(二分图)