hdu 3277 Marriage Match III【并查集+最大流Dinic+拆点+建图+二分查找】

Marriage Match III

Time Limit: 10000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2019    Accepted Submission(s): 593

Problem Description

Presumably, you all have known the question of stable marriage match. A girl will choose a boy; it is similar as the ever game of play-house . What a happy time as so many friends play 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. As 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. On the other hand, in order to play more times of marriage match, every girl can accept any K boys. If a girl chooses a boy, the boy must accept her unconditionally whether they had quarreled before or not. 

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 an integer T, means the number of test cases. 
Each test case starts with three integer n, m, K and f in a line (3<=n<=250, 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 1 2

1 1

2 3

3 2

4 2

4 4

1 4

2 3

Sample Output

3

Author

starvae

Source

HDOJ Monthly Contest – 2010.01.02

 

题目大意:有n*2个小孩,其中有n个女孩,有n个男孩,这些小孩玩过家家游戏,女孩选择和自己没吵过架的,或者选择和自己吵过架的但是这种情况有人数上限为k,然后对应如果当前女孩的朋友有一些没吵过架的男生,那么这个女孩也可以当做这些男孩和自己没吵过架,每一轮每个女孩都必须找一个男孩过家家,下一轮就不能选择之前自己选过的男孩进行过家家游戏,问最多能玩多少轮。


思路:


1、首先对于这一共能玩多少轮的问题,虽然一共只有250轮作为最大情况,然而我们每一次枚举一个数字作为可能解去进行判断的话,次数会比较多,O(n*n*n*M)是会超时的,所以我们要将这一维度时间复杂度降下去,因为其如果玩5轮的时候可以,那么玩4、3、2、1轮游戏都可以,如果玩7轮不可以,那么8、9.........都不可以,其满足递减性,那么我们可以进行二分查找。每一次枚举出一个当前情况进行判断,时间复杂度:O(logn*n^2m)


2、对于二分出的当前值mid,我们要跑最大流并判断是否可行,那么建图:

①建立源点S,连入各个女生,其权值设定为mid,表示一共玩mid轮游戏。

②建立汇点T,将各个男生连入汇点,其权值设定为mid,表示一共玩mid轮游戏。

③将每个女生节点拆点,一分为三,女生节点设定为u,女生选择没有吵过架的男生的节点设定为uu,女生选择吵过架的男生的节点设定为uuu。

④那么将女生节点u连入uu,其权值为mid,将女生节点u连入uuu,其权值为k,表示限制这个女生只能选k个吵过架的男生。

⑤那么对应,如果女生u和男生j没有吵过架,就用uu连入j,权值为1,表示这个女生只能选一次这个男生,同理,如果吵过架,就用uuu连入j,权值为1.


3、那么在建图之前,必须要处理的一个问题就是每个女生到底能有多少种选择的男生没有吵过架(因为女生的朋友如果和这个男生没有吵过架,那么就相当于这个女生和这个男生也没有吵过架)。那么我们建立并查集,如果两个女生是朋友,就加入到一个集合中,对于一个集合中的女生,其互相之间的选择都是想同的,O(n^3)处理一下就好。


4、那么整体思路就构建完毕了,剩下的内容就是写代码了.


Ac代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
struct node
{
    int from;
    int to;
    int w;
    int next;
}e[1212121];
int divv[25500];
int head[25500];
int cur[25500];
int f[25500];
int map[355][355];
int n,m,kk,ff,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;
}
void getmap(int mid)
{
    cont=0;
    memset(head,-1,sizeof(head));
    ss=4*n+1;
    tt=ss+1;
    for(int i=1;i<=n*3;i+=3)
    {
        add(ss,i,mid);
        add(i,ss,0);
    }
    for(int i=1;i<=n*3;i+=3)
    {
        for(int j=1;j<=n;j++)
        {
            if(map[i/3+1][j]==1)
            {
                add(i+1,j+3*n,1);
                add(j+3*n,i+1,0);
            }
            else
            {
                add(i+2,j+3*n,1);
                add(j+3*n,i+2,0);
            }
        }
        add(i,i+1,mid);
        add(i+1,i,0);
        add(i,i+2,kk);
        add(i+2,i,0);
    }
    for(int i=1;i<=n;i++)
    {
        add(i+3*n,tt,mid);
        add(tt,i+3*n,0);
    }
}
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(u==tt)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 Slove(int mid)
{
    getmap(mid);
    int ans=0;
    while(makedivv()==1)
    {
        memcpy(cur,head,sizeof(head));
        ans+=Dfs(ss,INF,tt);
    }
    if(ans>=mid*n)return 1;
    else return 0;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(map,0,sizeof(map));
        scanf("%d%d%d%d",&n,&m,&kk,&ff);
        for(int i=0;i<=n;i++)f[i]=i;
        for(int i=0;i<m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            map[x][y]=1;
        }
        for(int i=0;i<ff;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;
                        }
                    }
                }
            }
        }
        int ans=0;
        int mid;
        int l=0;
        int r=25000;
        while(r>=l)
        {
            mid=(l+r)/2;
            if(Slove(mid)==1)
            {
                ans=mid;
                l=mid+1;
            }
            else
            {
                r=mid-1;
            }
        }
        printf("%d\n",ans);
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值