1. 程式人生 > >hdu 3081 Marriage Match II (二分+網路流做法)

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