HDU-3081 Marriage Match II (最大流,二分答案,并查集)
Posted kangkang-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU-3081 Marriage Match II (最大流,二分答案,并查集)相关的知识,希望对你有一定的参考价值。
题目链接:HDU-3081 Marriage Match II
题意
有$n$个男孩和$n$个女孩玩配对游戏,每个女孩有一个可选男孩集合(即每轮游戏的搭档可从集合中选择),已知有些女孩之间是朋友(这里的朋友关系是相互的,即a和b是朋友,a和c是朋友,那么b和c也是朋友),那么她们可以共享男孩集合,即这些男孩集合的并集成为她们各自的可选男孩集合,如果某一轮女孩选择了一个男孩作为搭档,则这个男孩后面不能再作为这个女孩的搭档,问游戏最多进行几轮。
思路
女孩朋友之间共享男孩集合问题可以用并查集解决,将互为朋友的女孩放到一个并查集中,最后检查女孩两两之间是否在同一个并查集,是则说明她们是朋友,遍历$n$个男孩,合并她们与男孩之间的关系。
假设当前二分尝试的轮数为$K$,源点为$s$,汇点为$t$,用$(始点,终点,容量)$表示边,建立流网络如下:
若男孩$v$在女孩$u$的可选男孩集合中,连边$(u,v,1)$,表示女孩$u$只能选男孩$v$一次;
对于每个女孩$u$,连边$(s,u,K)$,表示每个女孩需要选择$K$个男孩作为$K$轮的搭档;
对于每个男孩$v$,连边$(v, t, K)$,表示每个男孩只能被$K$个女孩选到。
如果这个流网络的最大流为$n\times K$,那么说明游戏进行$K$轮是可行的。
为什么$s$的出边和$t$的入边流量都为$K$(将之简称为满流)就说明游戏进行$K$轮是可行的?
实际上若$K>1$时满流的话说明$K=1$也是可以满流的,因为$K=1$满流是更弱的要求,$K=1$满流显然等价于可以进行一轮;如果$K=2$可以满流的话说明在$K=1$的流量流过之后(1轮),女孩男孩之间连边的残余容量可以再满足一次$K=1$的流量流过,即可以再进行一轮(2轮),以此类推得出$s$的出边和$t$的入边容量都为$K$时能满流的话,则游戏能进行$K$轮。那么我们不断二分尝试$K$,找到能满流的最大$K$,就是最大的游戏轮数。
上面的说明并不是严谨的数学证明,谁会严谨证明欢迎交流。
代码实现
#include <iostream> #include <cstdio> #include <cstring> #include <queue> using std::queue; const int INF = 0x3f3f3f3f, N = 220, M = 30000; int head[N], d[N], par[N], ht[N]; int s, t, tot, maxflow; bool dist[N][N]; struct Edge int to, cap, nex; edge[M]; queue<int> q; void add(int x, int y, int z) edge[++tot].to = y, edge[tot].cap = z, edge[tot].nex = head[x], head[x] = tot; edge[++tot].to = x, edge[tot].cap = 0, edge[tot].nex = head[y], head[y] = tot; bool bfs() memset(d, 0, sizeof(d)); while (q.size()) q.pop(); q.push(s); d[s] = 1; while (q.size()) int x = q.front(); q.pop(); for (int i = head[x]; i; i = edge[i].nex) int v = edge[i].to; if (edge[i].cap && !d[v]) q.push(v); d[v] = d[x] + 1; if (v == t) return true; return false; int dinic(int x, int flow) if (x == t) return flow; int rest = flow, k; for (int i = head[x]; i && rest; i = edge[i].nex) int v = edge[i].to; if (edge[i].cap && d[v] == d[x] + 1) k = dinic(v, std::min(rest, edge[i].cap)); if (!k) d[v] = 0; edge[i].cap -= k; edge[i^1].cap += k; rest -= k; return flow - rest; void init2() tot = 1, maxflow = 0; memset(head, 0, sizeof(head)); void init1(int n) for (int i = 0; i <= n + 1; i++) par[i] = i, ht[i] = 0; s = 0, t = n + 1; memset(dist, 0, sizeof(dist)); int Find(int x) if (par[x] == x) return x; else return par[x] = Find(par[x]); void unite(int x, int y) x = Find(x), y = Find(y); if (x == y) return ; if (ht[x] < ht[y]) par[x] = y; else par[y] = x; if (ht[x] == ht[y]) ht[x]++; bool solve(int n, int K) init2(); for (int i = 1; i <= n; i++) for (int j = n + 1; j <= 2 * n; j++) if (dist[i][j]) add(i, j, 1); add(s, i, K); for (int i = n + 1; i <= 2 * n; i++) add(i, t, K); while (bfs()) maxflow += dinic(s, INF); return (maxflow == n * K); int main() int T; scanf("%d", &T); while (T--) int n, m, f; scanf("%d %d %d", &n, &m, &f); init1(2 * n); for (int i = 0, u, v; i < m; i++) scanf("%d %d", &u, &v); dist[u][v+n] = true; for (int i = 0, u, v; i < f; i++) scanf("%d %d", &u, &v); unite(u, v); for (int i = 1; i <= n; i++) for (int j = i + 1; j <= n; j++) if (Find(i) == Find(j)) for (int k = n + 1; k <= 2 * n; k++) dist[i][k] = dist[j][k] = (dist[i][k] || dist[j][k]); int L = 0, R = 101; while (R - L > 1) int mid = (L + R) >> 1; if (solve(n, mid)) L = mid; else R = mid; printf("%d\n", L); return 0;
以上是关于HDU-3081 Marriage Match II (最大流,二分答案,并查集)的主要内容,如果未能解决你的问题,请参考以下文章
HDU-3081-Marriage Match 2(最大流, 二分答案, 并查集)
HDU 3081 Marriage Match II 二分图最大匹配
HDU 3081 Marriage Match II(网络流+并查集+二分答案)
HDU 3081 Marriage Match II 二分 + 网络流