nyoj-239 月老的难题 (二分图匹配—匈牙利算法 && 网络流—Dinic算法)

月老的难题

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 4
描述

月老准备给n个女孩与n个男孩牵红线,成就一对对美好的姻缘。

现在,由于一些原因,部分男孩与女孩可能结成幸福的一家,部分可能不会结成幸福的家庭。

现在已知哪些男孩与哪些女孩如果结婚的话,可以结成幸福的家庭,月老准备促成尽可能多的幸福家庭,请你帮他找出最多可能促成的幸福家庭数量吧。

假设男孩们分别编号为1~n,女孩们也分别编号为1~n。

输入
第一行是一个整数T,表示测试数据的组数(1<=T<=400)
每组测试数据的第一行有两个整数n,K,其中男孩的人数与女孩的人数都是n。(n<=500,K<=10 000)
随后的K行,每行有两个整数i,j表示第i个男孩与第j个女孩有可能结成幸福的家庭。(1<=i,j<=n)
输出
对每组测试数据,输出最多可能促成的幸福家庭数量
样例输入
1
3 4
1 1
1 3
2 2
3 2
样例输出
2


思路: 

       此题是经典的二分图匹配模板题,学习网络流后发现也可解(只需加一个超级源点和超级汇点,就变成了网路流的模板题)。刚好初学这两种算法,便将此题作为模板题整理下来;二分图匈牙利算法与网络流的Dinic算法耗时相当,但网络流(使用邻接表建图)的空间消耗较大;

       二分图匹配—匈牙利算法, 资料:http://kukumayas.iteye.com/blog/1075610 

       http://blog.csdn.net/niushuai666/article/details/7023101 


匈牙利算法代码:

#include 
#include 
#include 
#define N 520

using namespace std;

int P_Num;					// 人数 
vectorBoy[N];			// 临界表,保存匹配信息 
bool use[N];				// 标记数组 
int form[N];				// 父节点(i号女生对应的男生编号) 

bool Match(int cur){										// 匹配函数 
	for(int i = 0; i < Boy[cur].size(); i ++){				// 遍历所有能与cur男生匹配的女生 
		if(!use[ Boy[cur].at(i) ]){							// 判断该女生结点是否被判断过,没有进入 
			use[ Boy[cur].at(i) ] = 1;						// 标记该女生已经判断 
			if(form[ Boy[cur].at(i) ] == -1 || Match(form[ Boy[cur].at(i) ])){		// 如果该女生未被匹配,或者之前匹配该女生的男生可以匹配其它女生 
				form[ Boy[cur].at(i) ] = cur;				// 则将该女生匹配给cur男生
				return 1;									// 成功匹配返回1 
			}
		}
	}
	
	return 0;
}

int Hungary(){									// 匈牙利算法 
	int count = 0;								// 计数器 
	memset(form, -1, sizeof(form));				// form保存女生对应的男生的编号,初始化为-1 
	
	for(int i = 1; i <= P_Num; i ++){			// 遍历每个男生结点,与女生深搜匹配 
		memset(use, 0, sizeof(use));			// 匹配前先将标记数组清零,起作用是防止重复搜索某结点 
		count += Match(i);
	}
	
	return count;
}

int main() 
{
	int loop, G_Num;
	scanf("%d", &loop);
	while(loop --){
		int start, end;
		scanf("%d%d", &P_Num, &G_Num);
		
		for(int i = 1; i <= P_Num; i ++){			// 多组数据初始化,清空数组 
			Boy[i].clear();
		}
		
		for(int i = 0; i < G_Num; i ++){
			scanf("%d%d", &start, &end);
			Boy[start].push_back(end);
		}
		
		int ans = Hungary();
		
		printf("%d\n", ans);
	}
	
	return 0;
}


网络流代码:

#include 
#include 
#include 
#define INF 0x7fffffff
#define N 1020

using namespace std;

struct Node{
	int to;
	int weight;
	int next;
};

Node edge[N * 100];
int head[N];
int level[N];
int edge_n;

int min(int a, int b)
{
	return a < b ? a : b;
}

void AddEdge(int start, int end, int weight)
{
	edge[edge_n].to = end;
	edge[edge_n].weight = weight;
	edge[edge_n].next = head[start];
	head[start] = edge_n ++;

	edge[edge_n].to = start;
	edge[edge_n].weight = 0;
	edge[edge_n].next = head[end];
	head[end] = edge_n ++;
}

bool bfs(int start, int end)
{
	memset(level, 0, sizeof(level));
	queueq;

	q.push(start);
	level[start] = 1;

	int cur;
	while(!q.empty()){
		cur = q.front();
		q.pop();

		for(int i = head[cur]; i != -1; i = edge[i].next){
			int v = edge[i].to;
			int w = edge[i].weight;
			if(w > 0 && !level[v]){
				level[v] = level[cur] + 1;
				if(v == end){
					return 1;
				}
				q.push(v);
			}
		}
	}

	return 0;
}

int dfs(int cur, int end, int cp)
{
	if(cur == end){
		return cp;
	}

	int t, tmp = cp;
	for(int i = head[cur]; i != -1 && tmp; i = edge[i].next){
		int v = edge[i].to;
		int w = edge[i].weight;
		if(level[v] == level[cur] + 1 && w > 0){
			t = dfs(v, end, min(tmp, w));
			edge[i].weight -= t;
			edge[i ^ 1].weight += t;
			tmp -= t;
		}
	}

	return cp - tmp;
}

int Dinic(int start, int end)
{
	int ans = 0, min_w;
	while( bfs(start, end) ){
		while( min_w = dfs(start, end, INF) ){
			ans += min_w;
		}
	}

	return ans;
}

int main()
{
	int loop, ans;
	int p_num, ct;			// 男女人数,关系数目
	int boy_i, girl_i;
	scanf("%d", &loop);
	while(loop --){
		edge_n = 0;
		memset(head, -1, sizeof(head));

		scanf("%d%d", &p_num, &ct);
		while(ct --){						// 建图
			scanf("%d%d", &boy_i, &girl_i);
			AddEdge(boy_i, girl_i + p_num, 1);
		}

		int src = 0, des = p_num * 2 + 1;	// 超级源点,超级汇点
		for(int i = 1; i <= p_num; i ++){
			AddEdge(src, i, 1);
			AddEdge(i + p_num, des, 1);
		}
		ans = Dinic(src, des);
		printf("%d\n", ans);
	}

	return 0;
}


你可能感兴趣的:(图论)