hdu 3081 Marriage Match II【并查集+二分+最大流Dinic+建图】

Marriage Match II

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3597    Accepted Submission(s): 1165

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*2个小孩,其中有n个女孩,有n个男孩,这些小孩玩过家家游戏,女孩只选择和自己没吵过架的,或者是没和自己朋友吵过架的男孩子一起过家家,每一轮每个女孩都必须找一个男孩过家家,下一轮就不能选择之前自己选过的男孩进行过家家游戏,问最多能玩多少轮。


输入:t,表示组数,接下来三个元素,分别表示有n对小孩,有m对男女孩么有吵过架,有f对朋友,接下来m行,每行两个元素,a,b表示a女孩和b男孩没有吵过架,接下来f行,每行两个元素a,b表示a女孩和b女孩是朋友。


暴力二分匹配+并查集做法:http://blog.youkuaiyun.com/mengxiang000000/article/details/52103285


思路:


1、设定map【i】【j】表示女孩i可以选择男孩j过家家。

①因为女孩可以选择的男孩有两种:一种是其朋友可以选择的男孩,一种是和自己没有吵过架的男孩,第二种可以直接在输入的时候就处理,而第一种,我们可以通过多种方式来解决,这里我们来尝试用并查集搞定这个问题,在输入f对朋友关系的时候,我们将是朋友关系的两个人加入一个集合中,然后N^2枚举i,j女孩,如果i,j在一个集合中,那么再用一层for同步两个人能够选择的男孩。{(ifmap【i】【k】==1)map【j】【k】=1}时间复杂度O(N^3)因为N并不大,所以还是能够撑得住的。


2、然后解决能够玩这个游戏多少轮的问题,敲了发现,二分+最大流真的是耗键盘.................

我们二分查找合法解,对应mid,建图如下方式:

①源点连接各个女孩,其权值为mid,表示这些女孩想要进行mid轮游戏。

②每个女孩连接其map【i】【j】==1(也就是能够选择的男孩)节点,其权值为1,表示女孩和这个男孩过过一次家家之后,就不在选他了。

③每个男孩连接汇点,其权值为mid,表示这些男孩想要进行mid轮游戏。


3、然后对应当前mid值跑一遍最大流Dinic 如果最大流maxflow==n*mid,也就是说当前mid值是合法的,也就是说这n*2个孩子确实能玩mid轮游戏,那么将mid记录进ans.继续二分,直到不能二分为止,输出ans、


Ac代码;


#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
struct node
{
    int from;
    int to;
    int w;
    int next;
}e[1212121];
int divv[25000];
int f[21500];
int head[25000];
int cur[25000];
int map[255][255];
int n,c,d,ss,tt,cont;
void add(int from,int to,int w)
{
    e[cont].to=to;
    e[cont].w=w;
    e[cont].next=head[from];
    head[from]=cont++;
}
int find(int a)
{
    int r=a;
    while(f[r]!=r)
    r=f[r];
    int i=a;
    int j;
    while(i!=r)
    {
        j=f[i];
        f[i]=r;
        i=j;
    }
    return r;
}
void merge(int a,int b)
{
    int A,B;
    A=find(a);
    B=find(b);
    if(A!=B)
    f[B]=A;
}
int makedivv()  
{  
    memset(divv,0,sizeof(divv));  
    divv[ss]=1;  
    queue<int >s;  
    s.push(ss);  
    while(!s.empty())  
    {  
        int u=s.front();  
        if(u==tt)return 1;  
        s.pop();  
        for(int i=head[u];i!=-1;i=e[i].next)  
        {  
            int v=e[i].to;  
            int w=e[i].w;  
            if(w&&divv[v]==0)  
            {  
                divv[v]=divv[u]+1;  
                s.push(v);  
            }  
        }  
    }  
    return 0;  
}  
int Dfs(int u,int maxflow,int tt)  
{  
    if(tt==u)return maxflow;  
    int ret=0;  
    for(int &i=cur[u];i!=-1;i=e[i].next)  
    {  
        int v=e[i].to;  
        int w=e[i].w;  
        if(w&&divv[v]==divv[u]+1)  
        {  
            int f=Dfs(v,min(maxflow-ret,w),tt);  
            e[i].w-=f;  
            e[i^1].w+=f;  
            ret+=f;  
            if(ret==maxflow)return ret;  
        }  
    }  
    return ret;  
}  
int Dinic(int mid)
{
    ss=n*2+1;
    tt=ss+1;
    cont=0;
    memset(head,-1,sizeof(head));
    for(int i=n+1;i<=2*n;i++)
    {
        add(i,tt,mid);
        add(tt,i,0);
    }
    for(int i=1;i<=n;i++)
    {
        add(ss,i,mid);
        add(i,ss,0);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(map[i][j]==1)
            {
                int u=i,v=j+n;
                add(u,v,1);
                add(v,u,0);
            }
        }
    }
    int ans=0;
    while(makedivv()==1)
    {
        memcpy(cur,head,sizeof(head));
        ans+=Dfs(ss,INF,tt);
    }
    //printf("%d %d\n",mid,ans);
    if(ans==mid*n)return 1;
    else return 0;
}
void Slove()
{
    int l=0;
    int r=20000;
    int ans=-1;
    int mid;
    while(r>=l)
    {
        mid=(l+r)/2;
        if(Dinic(mid)==1)
        {
            l=mid+1;
            ans=mid;
        }
        else r=mid-1;
    }
    if(ans==-1)
    {
        printf("0\n");
    }
    else
    printf("%d\n",ans);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(map,0,sizeof(map));
        for(int i=1;i<=215;i++)f[i]=i;
        scanf("%d%d%d",&n,&c,&d);
        for(int i=0;i<c;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            map[x][y]=1;
        }
        for(int i=0;i<d;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            merge(x,y);
        }
        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;
                        }
                        if(map[j][k]==1)
                        {
                            map[i][k]=1;
                        }
                    }
                }
            }
        }
        Slove();
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值