题目链接:https://vjudge.net/problem/HDU-3081
Marriage Match II
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4493 Accepted Submission(s): 1488
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?
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.
题意:
有n个女生和n个男生,有两种关系:1)女生a与男生b没有吵过架, 2)女生a与女生b是朋友。并且朋友关系是互相的且可以传递的(注意:只有女生才拥有朋友关系,男生没有这一个概念)。接着女生挑选男生作为男朋友,可以挑选和她没有吵过架的或者没有跟她朋友吵过架的男生。当所有女生都选完男朋友之后,一轮结束,再进行下一轮匹配挑选,规定选择过的伴侣不能再选。问:最多能进行多少轮匹配?
传递闭包 + 最大匹配:
1.根据题目意思,可以先用Flyod算法求出传递闭包。
2.由于在每一轮的匹配中,每个女生都要求能够找到男朋友。所以我们可以建立二分图,求出最大匹配是否完全匹配。
3.如果第2步中求出的是完全匹配,那么表示这一轮的匹配是有效的。由于题目要求新一轮的匹配中,不能选择“前任”,那么我们就把这一轮所有匹配对断开关系,即去掉两者之间的边。然后重复第2步。直到求出的不是完全匹配。
代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int mod = 1e9+7; 17 const int MAXM = 1e5+10; 18 const int MAXN = 2e2+10; 19 20 int uN, vN; 21 int maze[MAXN][MAXN], id[MAXN][MAXN], link[MAXN]; 22 bool vis[MAXN]; 23 24 bool dfs(int u) 25 { 26 for(int i = uN+1; i<=uN+vN; i++) 27 if(maze[u][i] && !vis[i]) 28 { 29 vis[i] = true; 30 if(link[i]==-1 || dfs(link[i])) 31 { 32 link[i] = u; 33 return true; 34 } 35 } 36 return false; 37 } 38 39 bool hungary() 40 { 41 int ret = 0; 42 memset(link, -1, sizeof(link)); 43 for(int i = 1; i<=uN; i++) 44 { 45 memset(vis, 0, sizeof(vis)); 46 if(!dfs(i)) return false; 47 } 48 return true; 49 } 50 51 void flyod(int N) 52 { 53 for(int k = 1; k<=N; k++) 54 for(int i = 1; i<=N; i++) 55 for(int j = 1; j<=N; j++) 56 maze[i][j] = maze[i][j]||(maze[i][k]&&maze[k][j]); 57 } 58 59 int main() 60 { 61 int T, n, m, f; 62 scanf("%d", &T); 63 while(T--) 64 { 65 scanf("%d%d%d", &n,&m,&f); 66 uN = vN = n; 67 memset(maze, 0, sizeof(maze)); 68 for(int i = 1; i<=m; i++) 69 { 70 int u, v; 71 scanf("%d%d", &u, &v); 72 maze[u][uN+v] = 1; 73 } 74 for(int i = 1; i<=f; i++) 75 { 76 int u, v; 77 scanf("%d%d", &u, &v); 78 maze[u][v] = maze[v][u] = 1; 79 } 80 81 flyod(uN+vN); 82 int round = 0; 83 while(hungary()) 84 { 85 round++; 86 for(int i = uN+1; i<=uN+vN; i++) 87 maze[link[i]][i] = 0; 88 } 89 printf("%d\n", round); 90 } 91 }
传递闭包 + 二分 + 最大流:
1.同样,先用Flyod算法求出传递闭包。
2.二分答案,即轮数,然后检测是否合法,以此缩小范围,最终得出最优解。
3.怎么检测是否合法呢?答:利用最大流算法进行检测:
1) 建立超级源点,超级源点连向每个女生,边权为二分的轮数mid,表明这个女生要有过mid个男朋友;
2) 建立超级汇点,每个男生连向超级汇点,且边权也为mid,表明这个男生要有mid个女朋友;
3) 然后如果女生girl能挑选男生boy,那么就连一条边u-->v,边权为1,表明,该男女只能匹配一次。
4) 跑最大流算法,如果最大流maxflow==n*mid,则表明当前轮数mid是合法的,否则不合法。
代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int mod = 1e9+7; 17 const int MAXM = 1e5+10; 18 const int MAXN = 2e2+10; 19 20 int uN, vN; 21 int maze[MAXN][MAXN]; 22 int gap[MAXN], dis[MAXN], pre[MAXN], cur[MAXN]; 23 int flow[MAXN][MAXN]; 24 25 int sap(int start, int end, int nodenum) 26 { 27 memset(cur, 0, sizeof(cur)); 28 memset(dis, 0, sizeof(dis)); 29 memset(gap, 0, sizeof(gap)); 30 memset(flow, 0, sizeof(flow)); 31 int u = pre[start] = start, maxflow = 0, aug = INF; 32 gap[0] = nodenum; 33 34 while(dis[start]<nodenum) 35 { 36 loop: 37 for(int v = cur[u]; v<nodenum; v++) 38 if(maze[u][v]-flow[u][v]>0 && dis[u] == dis[v]+1) 39 { 40 aug = min(aug, maze[u][v]-flow[u][v]); 41 pre[v] = u; 42 u = cur[u] = v; 43 if(v==end) 44 { 45 maxflow += aug; 46 for(u = pre[u]; v!=start; v = u, u = pre[u]) 47 { 48 flow[u][v] += aug; 49 flow[v][u] -= aug; 50 } 51 aug = INF; 52 } 53 goto loop; 54 } 55 56 int mindis = nodenum-1; 57 for(int v = 0; v<nodenum; v++) 58 if(maze[u][v]-flow[u][v]>0 && mindis>dis[v]) 59 { 60 cur[u] = v; 61 mindis = dis[v]; 62 } 63 if((--gap[dis[u]])==0) break; 64 gap[dis[u]=mindis+1]++; 65 u = pre[u]; 66 } 67 return maxflow; 68 } 69 70 bool test(int mid) 71 { 72 int start = 0, end = uN+vN+1; 73 for(int i = 1; i<=uN; i++) maze[start][i] = mid; 74 for(int i = 1; i<=vN; i++) maze[uN+i][end] = mid; 75 return sap(start, end, uN+vN+2)==uN*mid; 76 } 77 78 void flyod(int N) 79 { 80 for(int k = 1; k<=N; k++) 81 for(int i = 1; i<=N; i++) 82 for(int j = 1; j<=N; j++) 83 maze[i][j] = maze[i][j]||(maze[i][k]&&maze[k][j]); 84 } 85 86 int main() 87 { 88 int T, n, m, f; 89 scanf("%d", &T); 90 while(T--) 91 { 92 scanf("%d%d%d", &n,&m,&f); 93 uN = vN = n; 94 memset(maze, 0, sizeof(maze)); 95 for(int i = 1; i<=m; i++) 96 { 97 int u, v; 98 scanf("%d%d", &u, &v); 99 maze[u][uN+v] = 1; 100 } 101 for(int i = 1; i<=f; i++) 102 { 103 int u, v; 104 scanf("%d%d", &u, &v); 105 maze[u][v] = maze[v][u] = 1; 106 } 107 108 flyod(uN+vN); 109 int l = 0, r = uN; 110 while(l<=r) 111 { 112 int mid = (l+r)/2; 113 if(test(mid)) 114 l = mid + 1; 115 else 116 r = mid - 1; 117 } 118 printf("%d\n", r); 119 } 120 }