hdu 3081 Marriage Match II (二分+网络流做法)

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

Presumably, you all have known the question of stable marriage match. A girl will choose a boy; it is similar as the game of playing house we used to play when we are kids. What a happy time as so many friends playing together. And it is normal that a fight or a quarrel breaks out, but we will still play together after that, because we are kids.
Now, there are 2n kids, n boys numbered from 1 to n, and n girls numbered from 1 to n. you know, ladies first. So, every girl can choose a boy first, with whom she has not quarreled, to make up a family. Besides, the girl X can also choose boy Z to be her boyfriend when her friend, girl Y has not quarreled with him. Furthermore, the friendship is mutual, which means a and c are friends provided that a and b are friends and b and c are friend.
Once every girl finds their boyfriends they will start a new round of this game—marriage match. At the end of each round, every girl will start to find a new boyfriend, who she has not chosen before. So the game goes on and on.
Now, here is the question for you, how many rounds can these 2n kids totally play this game?

Input

There are several test cases. First is a integer T, means the number of test cases.
Each test case starts with three integer n, m and f in a line (3<=n<=100,0<m<n*n,0<=f<n). n means there are 2*n children, n girls(number from 1 to n) and n boys(number from 1 to n).
Then m lines follow. Each line contains two numbers a and b, means girl a and boy b had never quarreled with each other.
Then f lines follow. Each line contains two numbers c and d, means girl c and girl d are good friends.

Output

For each case, output a number in one line. The maximal number of Marriage Match the children can play.

Sample Input

 

1 4 5 2 1 1 2 3 3 2 4 2 4 4 1 4 2 3

Sample Output

 

2

在二分图多重匹配更新过一次该题的解法,现在更新一下网络流的解法

建图:

源点s为0,汇点t为2*n+1.女孩编号1到n,男孩编号n+1到2*n. 假设我们当前二分尝试的轮数为K(即能够进行K轮匹配):

首先如果女孩i可能选择男孩j,那么就有边(i, j+n, 1).且源点到每个女孩i有边(s,i,K),每个男孩j到汇点t有边(j+n,t,K).

如果最大流==K*n,那么就表示可以进行最少K轮匹配.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
using namespace std;
const int maxn = 205;
const int inf = 0x3f3f3f3f;
int T, n, m, u, v, k,tot,ui;
int f[maxn],level[maxn], head[maxn];
bool vis[maxn][maxn],vis1[maxn][maxn];
struct node
{
	int to, next, cap;
}edge[maxn*maxn];
void addedge(int u, int v, int w,int &k)
{
	edge[k].to = v;
	edge[k].next = head[u];
	edge[k].cap = w;
	head[u] = k++;
	edge[k].to = u;
	edge[k].next = head[v];
	edge[k].cap = 0;
	head[v] = k++;
	return  ;
}
int _find(int u)
{
	if (u != f[u])
	{
		f[u] = _find(f[u]);
	}
	return f[u];
}
void init()
{
	memset(head, -1, sizeof(head));
	tot = 0;
}
int bfs(int s, int t)
{
	queue<int>pq;
	memset(level, 0, sizeof(level));
	level[s] = 1;
	pq.push(s);
	while (!pq.empty())
	{
		int v,w;
		int u = pq.front();
		pq.pop();
		if (u == t)
		{
			return 1;
		}
		for (int i = head[u]; i != -1; i = edge[i].next)
		{
			v = edge[i].to;
			w = edge[i].cap;
			if (level[v] == 0 && w != 0)
			{
				level[v] = level[u] + 1;
				pq.push(v);
			}
		}
	}
	return -1;
}
int dfs(int u, int des, int f) {
	if (u == des || f == 0) {
		return f;
	}
	int ret = 0;
	for (int k = head[u]; k != -1; k = edge[k].next) 
	{
		int v = edge[k].to, w = edge[k].cap;
		if (level[v] == level[u] + 1 && w != 0) 
		{
			int MIN = min(f - ret, w);
			w = dfs(v, des, MIN);
			if (w > 0)
			{
				edge[k].cap -= w;
				edge[k ^ 1].cap += w;
				ret += w;
				if (ret == f) {
					return ret;
				}
			}
			else level[v] = -1;
			if (f == 0) break;
		}
	}
	if (ret == 0) level[u] = -1;
	return ret;
}
int maxflow(int s, int t)
{
	int flow = 0;
	while (bfs(s, t) != -1)
	{
		flow += dfs(s, t, inf);
	}
	return flow;
}
void build(int c)
{
	init();
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1 + n; j <= n + n; j++)
		{
			vis[i][j] = vis1[i][j];
		}
	}
	int st = 0, ed = 2 * n + 1;
	for (int i = 1; i <= n; i++)
	{
		addedge(st, i, c, tot);     //源点向女生连线   容量为mid
		addedge(i + n, ed, c, tot);  //男生向汇点连线  容量为mid
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1 + n; j <= n + n; j++)
		{
			if (vis[i][j])
			{
				addedge(i, j, 1, tot);   //女生向男生连线  容量

为1
			}
		}
	}
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			if (_find(i) == _find(j))
			{
				for (int k = 1 + n; k <= 2 * n; k++)
				{
					if (vis[i][k] && !vis[j][k])
					{
						addedge(j, k, 1, tot);    //两

个女生为好朋友, 女生向男生连线,容量为1
						vis[j][k] = 1;
					}
				}
			}
		}
	}
}
int main()
{
	//freopen("C:/input.txt", "r", stdin);
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d%d", &n, &m, &ui);
		for (int i = 1; i <= n; i++)
		{
			f[i] = i;
		}
		memset(vis, false, sizeof(vis));
		memset(vis1, false, sizeof(vis1));
		for (int i = 1; i <= m; i++)
		{
			scanf("%d%d", &u, &v);
			v += n;
			vis[u][v] = vis1[u][v] = true;
		}
		for (int i = 1; i <= ui; i++)
		{
			scanf("%d%d", &u, &v);
			int a = _find(u);
			int b = _find(v);
			f[a] = b;
		}
		int l = 0, r = n, ans = 0;
		while (l <= r)     //二分
		{
			int mid = (l + r) >> 1;
			build(mid);
			if (maxflow(0, 2 * n + 1) == n * mid)    //满流
			{
				ans = mid;
				l = mid + 1;
			}
			else
			{
				r = mid - 1;
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Evildoer_llc/article/details/83246796