[kuangbin带你飞]专题十一 网络流\N HDU 3081 Marriage Match II

题目描述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? 
InputThere 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. 
outputFor 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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

/*
这道题乍看之下好像是二分匹配,但仔细一想是不太一样的。
考虑用网络流做,首先女生可以与她朋友能配对的男生配对,这样需要用并查集保存他们可以配对的关系,这一点应该不难想到。
接下来就是建图了,每个女生与可以配对的男生(包括朋友的可配对的男生)之间建边,容量为 1。
这样需要考虑的就是源点和女生、男生和汇点之间该如何建边了。

本来考虑到n个女生和n个男生不重复完全配对最多只能进行n轮,想到可以将源点和女生、男生和汇点之间的容量赋为n,求出最大流。
但是发现这样是不行的,如果有女生或者男生无法配对到,即一轮都无法进行,这样得到的答案就不对了。
为了保证每一轮所有女生和男生都能匹配到,我们需要二分源点和女生、男生和汇点之间的容量k,并且需要保证满流。
找到最大的满足条件的k就是答案。

解法的正确性可以用数学归纳法证明,
	当k=1时,转化为一个二分匹配,如果满流,就说明可以进行一轮。
	当k-1轮可以实现时(k-1满流),如果容量为k时满流,说明也可以实现k轮。
	这样就证明了正确性。

由这个解法我们也可以想到用二分匹配的方法来解决:
	进行二分图的最大匹配,在匹配完成后判断匹配数是否等于n,不是的话说明GAME OVER 求得答案,
	是的话说明游戏能完成,然后进行删边操作,再继续匹配,直到匹配数<n为止。
*/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int MAXN = 2010;
const int MAXM = 1200012;
const int INF = 0x3f3f3f3f;
struct Edge {
	int to, next, cap, flow;
} edge[MAXM];
int tol;
int head[MAXN];
void init() {
	tol = 2;
	memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int w, int rw = 0) {
	edge[tol].to = v;
	edge[tol].cap = w;
	edge[tol].flow = 0;
	edge[tol].next = head[u];
	head[u] = tol++;
	edge[tol].to = u;
	edge[tol].cap = rw;
	edge[tol].flow = 0;
	edge[tol].next = head[v];
	head[v] = tol++;
}
int Q[MAXN];
int dep[MAXN], cur[MAXN], sta[MAXN];
bool bfs(int s, int t, int n) {
	int front = 0, tail = 0;
	memset(dep, -1, sizeof(dep[0])*(n+1));
	dep[s] = 0;
	Q[tail++] = s;
	while(front < tail) {
		int u = Q[front++];
		for(int i = head[u]; i != -1; i = edge[i].next) {
			int v = edge[i].to;
			if(edge[i].cap > edge[i].flow && dep[v] == -1)                         {
				dep[v] = dep[u] + 1;
				if(v == t) return true;
				Q[tail++] = v;
			}
		}
	}
	return false;
}
int dinic(int s, int t, int n) {
	int maxflow = 0;
	while(bfs(s, t, n)) {
		for(int i = 0; i < n; i++) cur[i] = head[i];
		int u = s, tail = 0;
		while(cur[s] != -1) {
			if(u == t) {
				int tp = INF;
				for(int i = tail-1; i >= 0; i--)
					tp = min(tp, edge[sta[i]].cap-edge[sta[i]].flow);
				maxflow+=tp;
				for(int i = tail-1; i >= 0; i--) {
					edge[sta[i]].flow+=tp;
					edge[sta[i]^1].flow-=tp;
					if(edge[sta[i]].cap-edge[sta[i]].flow==0)
						tail = i;
				}
				u = edge[sta[tail]^1].to;
			} else if(cur[u] != -1 && edge[cur[u]].cap > edge[cur[u]].flow && dep[u] + 1 == dep[edge[cur[u]].to]) {
				sta[tail++] = cur[u];
				u = edge[cur[u]].to;
			} else {
				while(u != s && cur[u] == -1)
					u = edge[sta[--tail]^1].to;
				cur[u] = edge[cur[u]].next;
			}
		}
	}
	return maxflow;
}
int n,m,f;
int fa[MAXN];
int map[120][120];

int find(int x) {
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}

void unio(int x, int y) {
	int a = find(x), b = find(y);
	if(a != b)
		fa[a] = b;
}

void build(int k) {
	init();
	for(int i = 1; i <= n; ++i) {
		addedge(0, i, k);
		addedge(i + n, 2 * n + 1, k);
	}
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= n; ++j)
			if(map[i][j])
				addedge(i , n + j, 1);
}
int main() {
	int t;
	scanf("%d", &t);
	while(t--) {
		scanf("%d%d%d", &n, &m, &f);
		for(int i = 1; i <= n; ++i)
			fa[i] = i;
		memset(map, 0, sizeof(map));
		int a, b;
		for(int i = 1; i <= m; ++i) {
			scanf("%d%d", &a, &b);
			map[a][b] = 1;
		}
		for(int i = 1; i <= f; ++i) {
			scanf("%d%d", &a, &b);
			unio(a, b);
		}
		for(int i = 1; i <= n; ++i)
			for(int j = 1; j <= n; ++j)
				if(find(i) == find(j))
					for(int k = 1; k <= n; ++k)
						if(map[i][k])
							map[j][k] = 1;
		int s = 0, t = n, ans;
		while(s <= t) {
			int mid = (s + t) / 2;
			build(mid);
			if(mid * n == dinic(0, 2 * n + 1, 2 * n + 2)) {
				ans = mid;
				s = mid + 1;
			} else
				t = mid - 1;
		}
		cout << ans << endl;
	}
	return 0;
}

 

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REaDME.md或论文文件(如有),本项目仅用作交学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值