HDU-3081 Marriage Match II (二分 + 并查集 + 最大流)

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;
}

猜你喜欢

转载自blog.csdn.net/henu_jizhideqingwa/article/details/81986120
今日推荐