http://acm.hdu.edu.cn/showproblem.php?pid=3081
题意:
n个女孩n个男孩,女孩可以找没有与她没有争吵的男孩结婚,也可以与与她的好朋友没有争吵的男孩结婚,问这2n的男女两两不重复的能结几次
思路:
一开始没想到最大流,看了别人博客才知道,先用二分查找可结婚的次数,然后建边,把源点S与女孩相连,权值为查找的数字,而女孩用并查集找到可以结婚的男孩与他们连边,权值为1,男孩与汇点T连边,跑最大流即可
#include <iostream>
#include <string.h>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <stdio.h>
#include <deque>
using namespace std;
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define maxn 150
#define eps 0.00000001
#define PI acos(-1.0)
#define M 1000000007
struct Edge{
int v, w, nxt;
}edge[800005];
struct Point{
int u, v;
}point[800005];
int n, m, f, vis[maxn][maxn], ff[maxn];
int S, T, tot, head[40005];
int dis[40005], cur[40005];
void addEdge(int u, int v, int w) {
edge[tot].v = v;
edge[tot].w = w;
edge[tot].nxt = head[u];
head[u] = tot ++;
edge[tot].v = u;
edge[tot].w = 0;
edge[tot].nxt = head[v];
head[v] = tot ++;
}
void init() {
tot = 0;
memset(head, -1, sizeof(head));
memset(vis, 0, sizeof(vis));
}
int Find(int x) {
int r = x;
while(r != ff[r])
r = ff[r];
int j = x;
while(j != ff[j]) {
int k = ff[j];
ff[j] = r;
j = k;
}
return r;
}
void Join(int x, int y) {
int xx = Find(x), yy = Find(y);
if(xx != yy) {
if(xx < yy)
ff[xx] = yy;
else
ff[yy] = xx;
}
}
int bfs() {
memset(dis, -1, sizeof(dis));
dis[T] = 0;
queue<int> que;
que.push(T);
while(!que.empty()) {
int u = que.front(); que.pop();
for (int i = head[u]; i + 1; i = edge[i].nxt) {
if(dis[edge[i].v] == -1 && edge[i ^ 1].w > 0) {
dis[edge[i].v] = dis[u] + 1;
que.push(edge[i].v);
}
}
}
return dis[S] != -1;
}
int dfs(int u, int flow) {
if(u == T) return flow;
int belta = flow;//belta 可以看作是从源点到此处的最大容量
for (int &i = cur[u]; i + 1; i = edge[i].nxt) {
//cur[i] = i;
if(dis[u] == dis[edge[i].v] + 1 && edge[i].w > 0) {
int d = dfs(edge[i].v, min(belta, edge[i].w));
edge[i].w -= d; edge[i ^ 1].w += d;
belta -= d;
if(belta == 0) break;//如果容量为0,那么这条路就不再有增广路,break
}
}
return flow - belta;//返回的是 最大容量-剩余容量
}
int dinic() {
int ans = 0;
while(bfs()) {
for (int i = 0; i <= T; i ++)
cur[i] = head[i];
ans += dfs(S, INF);
}
return ans;
}
bool Build(int Mid) {
tot = 0;
memset(head, -1, sizeof(head));
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; i ++) {
addEdge(S, i, Mid);
addEdge(i + n, T, Mid);
}
for (int i = 0; i < m; i ++) {
int u = point[i].u, v = point[i].v;
for (int j = 1; j <= n; j ++) {
if(Find(u) == Find(j) && !vis[j][v]) {
addEdge(j, v + n, 1);
vis[j][v] = 1;
}
}
}
if(dinic() >= Mid * n)
return 1;
else
return 0;
}
int main(int argc, const char * argv[]) {
int TT;
scanf("%d", &TT);
while(TT--) {
scanf("%d %d %d", &n, &m, &f);
S = 0; T = 2 * n + 1;
for (int i = 0; i <= T; i ++)
ff[i] = i;
for (int i = 0; i < m; i ++)
scanf("%d %d", &point[i].u, &point[i].v);
for (int i = 0; i < f; i ++) {
int ag, bg;
scanf("%d %d", &ag, &bg);
Join(ag, bg);
}
int l = 0, r = n, ans = 0;
while(l <= r) {
int mm = (l + r) >> 1;
if(Build(mm)) {
l = mm + 1;
ans = mm;
}
else r = mm - 1;
}
printf("%d\n", ans);
}
return 0;
}