HDU 3081 Marriage Match II (二分图,并查集)

HDU 3081 Marriage Match II (二分图,并查集)

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<nn,0<=f<n). n means there are 2n 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

Http

HDU:https://vjudge.net/problem/HDU-3081

Source

二分图,并查集

题目大意

有2*n个点,现在前n个点与后n个点有若干对应关系,求有多少种二分图完美匹配的方案

解决思路

首先不考虑朋友关系。我们可以每次做二分图匹配时,如果是完美匹配,就删掉那些匹配边,再跑二分图匹配,方案数+1。若不是完美匹配,则说明已经无解,退出即可。
再来考虑朋友关系。因为朋友关系是可以传递的,所以我们可以用并查集来维护再同一个朋友集合中的人。那么只要这一个朋友集合内有一个人可以连到u,则这个集合中的所有人都可以连到u。
最后是图论结构的选择。因为本题数据范围不大,所以可以采用邻接矩阵的方式,同时这也可以很方便地支持删除操作,还不需要判重。
另:这里用匈牙利算法实现二分图匹配,具体可以参考这两篇博客:
http://www.cnblogs.com/SYCstudio/p/7138221.html
http://www.cnblogs.com/SYCstudio/p/7138230.html
另:本题还可以用网络流最大流的方式解决,具体请看这篇文章

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxN=201;
const int maxM=maxN*maxN*2;
const int inf=2147483647;

int n,m;
int Map[maxN][maxN];
int Match[maxN];
int use[maxN];
int Mayuri[maxN];

int Find(int x);//并查集寻找祖先同时路径压缩
bool Hungary(int u);//匈牙利算法求解二分图匹配

int main()
{
    int T;
    scanf("%d",&T);
    while (T--)
    {
        memset(Map,0,sizeof(Map));//多组数据
        int f;
        scanf("%d%d%d",&n,&m,&f);
        for (int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            Map[u][v]=1;//先连上边
        }
        for (int i=1;i<=n;i++)//并查集初始化
            Mayuri[i]=i;
        for (int i=1;i<=f;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);//读入朋友关系
            int fu=Find(u);//合并并查集
            int fv=Find(v);
            if (fu!=fv)
                Mayuri[fu]=fv;
        }
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                if (Find(i)==Find(j))
                    for (int k=1;k<=n;k++)//如果在同一个集合中,只要有一个连了就都要连边
                        if (Map[i][k]==1)
                            Map[j][k]=1;
        int Ans=0;//统计方案数
        while (1)//多次求解二分图匹配
        {
            int matchcnt=0;//统计匹配个数
            memset(Match,-1,sizeof(Match));//匈牙利算法求解最大流
            for (int i=1;i<=n;i++)
            {
                memset(use,0,sizeof(use));
                if (Hungary(i))
                    matchcnt++;
            }
            if (matchcnt<n)//当匹配数小于n时,不存在完美匹配,退出
                break;
            for (int i=1;i<=n;i++)//删去本次匹配中用到的边
                Map[Match[i]][i]=0;
            Ans++;
        }
        printf("%d\n",Ans);
    }
    return 0;
}

int Find(int x)
{
    if (Mayuri[x]!=x)
        Mayuri[x]=Find(Mayuri[x]);
    return Mayuri[x];
}

bool Hungary(int u)
{
    for (int i=1;i<=n;i++)
        if ((Map[u][i]==1)&&(use[i]==0))
        {
            use[i]=1;
            if ((Match[i]==-1)||(Hungary(Match[i])))
            {
                Match[i]=u;
                return 1;
            }
        }
    return 0;
}
posted @ 2017-08-21 23:03 SYCstudio 阅读(...) 评论(...) 编辑 收藏