月老的难题
时间限制:
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 <stdio.h> #include <string.h> #include <vector> #define N 520 using namespace std; int P_Num; // 人数 vector<int>Boy[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 <stdio.h> #include <string.h> #include <queue> #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)); queue<int>q; 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; }
-
第一行是一个整数T,表示测试数据的组数(1<=T<=400)