hdu 3081 Marriage Match II 【图论-网络流-最大流+并查集】

来源:互联网 发布:淘宝旺旺官方下载 编辑:程序博客网 时间:2024/04/29 21:58
                                    Marriage Match II    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Problem Description
Presumably, you all have known the question of stable marriage match. A girl will choose a boy; it is similar as the game of playing house we used to play when we are kids. What a happy time as so many friends playing together. And it is normal that a fight or a quarrel breaks out, but we will still play together after that, because we are kids.
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?

Input
There are several test cases. First is a integer T, means the number of test cases.
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.

Output
For each case, output a number in one line. The maximal number of Marriage Match the children can play.

Sample Input
1
4 5 2
1 1
2 3
3 2
4 2
4 4
1 4
2 3

Sample Output
2

题目大意:有n个女孩和n个男孩玩配对游戏,游戏规则:
1)女孩选择与她没有任何冲突并且与她的朋友没有任何冲突的男孩
2)如何女孩a与女孩b认识,女孩b与女孩c认识,则a,b,c互相认识
然后从第一轮开始,每个女孩都要和一个不同的男孩配对.如果第一轮N个女孩都配对成功,那么就开始第二轮配对,女孩依然从自己的备选男孩集合中选择,但是不能选那些已经被该女孩在前几轮选择中选过的男孩了(比如i女孩在第一轮选了j男孩,那么i在第二轮就不能选j男孩了). 问你游戏最多能进行多少轮?

AC代码:

//并查集+最大流# include <iostream># include <cstdio># include <cstring># include <queue># include <algorithm>using namespace std;# define MAXN 1005# define MAXM 5000005# define INF 1 << 29struct EDGE{    int to;    int w;    int next;}edge[MAXM];int tot;int head[MAXN];int father[MAXN];int dis[MAXN];bool Graph[MAXN][MAXN];int min(int a, int b){    return a > b ? b : a;}void Init(){    memset(father, -1, sizeof(father));    memset(Graph, false, sizeof(Graph));}void DinicInit(){    tot = 0;    memset(head, -1, sizeof(head));}void Addedge(int u, int v, int w){    edge[tot].to = v;    edge[tot].w = w;    edge[tot].next = head[u];    head[u] = tot++;    edge[tot].to = u;    edge[tot].w = 0;    edge[tot].next = head[v];    head[v] = tot++;}/********************Dinic********************/bool Bfs(int s, int t){    memset(dis, 0, sizeof(dis));    queue <int> que;    dis[s] = 1;    que.push(s);    while (!que.empty())    {        int u = que.front(); que.pop();        for (int i = head[u]; i + 1; i = edge[i].next)        {            int v = edge[i].to;            if (!dis[v] && edge[i].w > 0)            {                dis[v] = dis[u] + 1;                if (v == t)                {                    return true;                }                que.push(v);            }        }    }    return false;}int Dfs(int u, int t, int f){    if (u == t)    {        return f;    }    int cost = 0;    for (int i = head[u]; i + 1; i = edge[i].next)    {        int v = edge[i].to;        int w = edge[i].w;        if (dis[v] == dis[u] + 1 && w > 0)        {            int d = Dfs(v, t, min(w, f - cost));            if (d > 0)            {                edge[i].w -= d;                edge[i ^ 1].w += d;                cost += d;                if (cost == f)                {                    break;                }                else                {                    dis[v] = -1;                }            }        }    }    return cost;}int Dinic(int s, int t){    int maxflow = 0;    while (Bfs(s, t))    {        maxflow += Dfs(s, t, INF);    }    return maxflow;}/********************Dinic********************//********************并查集********************/int Findroot(int x){    if (-1 == father[x])    {        return x;    }    else    {        return father[x] = Findroot(father[x]);    }}void Unite(int a, int b){    int f1 = Findroot(a);    int f2 = Findroot(b);    if (f1 != f2)    {        father[f1] = f2;    }}/******************并查集*************************/bool Solve(int s, int t, int n, int mid){    int i, j;    DinicInit(); //每次求最大流都要重新建边,因此需要清除之前的边的信息    for (i = 1; i <= n; i++)    {        Addedge(s, i, mid);    }    for (i = 1; i <= n; i++)    {        for (j = n + 1; j <= 2 * n; j++)        {            if (Graph[i][j])            {                Addedge(i, j, 1);            }        }    }    for (j = n + 1; j <= 2 * n; j++)    {        Addedge(j, t, mid);    }    int maxflow = Dinic(s, t);    //printf("maxflow = %d\n", maxflow);    return maxflow == n * mid;}int main(void){    int T;    scanf("%d", &T);    while (T--)    {        Init();        int i, j, k;        int n, m, f;        scanf("%d %d %d", &n, &m, &f);        for (i = 0; i < m; i++)        {            int u, v;            scanf("%d %d", &u, &v);            Graph[u][v + n] = 1;        }        for (i = 0; i < f; i++)        {            int u, v;            scanf("%d %d", &u, &v);            Unite(u, v);        }        for (i = 1; i <= n; i++)        {            for (j = 1; j <= n; j++)            {                if (Findroot(i) == Findroot(j)) //女孩属于同一组,则她们都与她们的男朋友有关                {                    for (k = n + 1; k <= n * 2; k++)                    {                        Graph[i][k] = Graph[j][k] = (Graph[i][k] || Graph[j][k]);                    }                }            }        }        int left = 0;        int right = 100;        int s = 0;        int t = 2 * n + 1;        while (left < right) //使用二分进行优化        {            int mid = left + (right - left + 1) / 2;            if (Solve(s, t, n, mid))            {                left = mid;            }            else            {                right = mid - 1;            }        }        printf("%d\n", left);    }    return 0;}
0 0