HDU3081Marriage Match II(二分答案+并查集+最大流SAP)经典

Marriage Match II

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 2507    Accepted Submission(s): 856

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

Author

starvae

题意:共有2*n个人,一半女一半男,女与男有m个关系,表示可以成为一对,接下来 f 对女的与女的  的朋友关系,如果a与b是朋友,那么表示a女与b女的相连男性也可以成为一对,同样b也与a的相连男性可成为一对,女的之间的朋友关系可以传递。一组配对情况为所有的女性都有一个与之配对的男性(一对一的关系),如果还有其他组配对情况,那么所有的女性配对不可以再与原来的男性配成对。问最多有多少组配对情况。

解析:对于女的与女的之间关系可以用并查集处理一下就OK 了,关键是如何得到多少组配对情况,那么分析一下,根据题意,每个人在每一组配对情况就是不相同的,那么如果有k组,则所有女性每人至少有k个不同的配对关系。同样,对于所有的男性配对女性也是同样的,至少有k个配对关系。就可以用最大流来做,女与源点相连,男与汇点相连的边容都为k,女与男配对的关系因为只能用一次,所以边容设为1。如果得到的最大流为 n*k 的值则成立。对于怎么枚举出k,则用二分答案。

#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define captype int

const int MAXN = 210;   //点的总数
const int MAXM = 40010;    //边的总数
const int INF = 1<<30;
struct EDG{
    int to,next;
    captype cap,flow;
} edg[MAXM];
int eid,head[MAXN];
int gap[MAXN];  //每种距离(或可认为是高度)点的个数
int dis[MAXN];  //每个点到终点eNode 的最短距离
int cur[MAXN];  //cur[u] 表示从u点出发可流经 cur[u] 号边
int pre[MAXN];

void init(){
    eid=0;
    memset(head,-1,sizeof(head));
}
//有向边 三个参数,无向边4个参数
void addEdg(int u,int v,captype c,captype rc=0){
    edg[eid].to=v; edg[eid].next=head[u];
    edg[eid].cap=c; edg[eid].flow=0; head[u]=eid++;

    edg[eid].to=u; edg[eid].next=head[v];
    edg[eid].cap=rc; edg[eid].flow=0; head[v]=eid++;
}
captype maxFlow_sap(int sNode,int eNode , int n){
    memset(gap,0,sizeof(gap));
    memset(dis,0,sizeof(dis));
    memcpy(cur,head,sizeof(head));
    pre[sNode]=-1;
    gap[0]=n;

    captype ans = 0;
    int u=sNode;
    while(dis[sNode]<n){
        if(u==eNode){
            captype mint = INF , mincap = INF;
            int minid ;
            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to])
            if(mint > edg[i].cap - edg[i].flow){
                mint = edg[i].cap - edg[i].flow ;
                minid=i;
            }
            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to]){
                edg[i].flow += mint;
                edg[i^1].flow -=mint;
            }
            ans += mint;
            u = edg[minid^1].to;
            continue;
        }
        bool flag=false;
        for(int i=cur[u]; i!=-1; i=edg[i].next)
        if(edg[i].cap-edg[i].flow>0 && dis[u]==dis[edg[i].to]+1){
            cur[u]=pre[edg[i].to]=i;
            flag=true;
            break;
        }
        if(flag){
            u=edg[cur[u]].to;
            continue;
        }
        int minh=n;
        for(int i=head[u]; i!=-1; i=edg[i].next)
            if(edg[i].cap-edg[i].flow>0 && minh>dis[edg[i].to]){
                minh=dis[edg[i].to];
                cur[u]=i;
            }
        gap[dis[u]]--;
        if(gap[dis[u]]==0)
            return ans;
        dis[u]=minh+1;
        gap[dis[u]]++;
        if(u!=sNode)
            u=edg[pre[u]^1].to;
    }
    return ans;
}
void changCap(int s,int t,int k){
    for(int i=head[s]; i!=-1; i=edg[i].next)
        edg[i].cap=k;
    for(int i=head[t]; i!=-1; i=edg[i].next)
        edg[i^1].cap=k;
    for(int i=0; i<eid; i++)//每一次都要清0
        edg[i].flow=0;
}
int father[MAXN];
int findfath(int x){
    if(x!=father[x])
        father[x]=findfath(father[x]);
    return father[x];
}
void link(int x,int y){
    x=findfath(x);
    y=findfath(y);
    father[x]=y;
}
int mapt[105][105];
void changMap(int n){
    int tmp[105][105]={0};
    for(int i=1; i<=n; i++) //对于在同一棵树上可以用一个点来替换(根节点)
        father[i]=findfath(i);
    for(int i=1; i<=n; i++){
        int ti=father[i];
        for(int j=1; j<=n; j++)
            tmp[ti][j]|=mapt[i][j];
    }
    for(int i=1; i<=n; i++){
        int ti=father[i];
        for(int j=1; j<=n; j++)
            mapt[i][j]=tmp[ti][j];
    }
}
int main(){
    int T;
    int n,m,f;
    int a,b;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&f);
        memset(mapt,0,sizeof(mapt));
        while(m--){
            scanf("%d%d",&a,&b);
            mapt[a][b]=1;
        }
        for(int i=1; i<=n; i++)
            father[i]=i;
        while(f--){
            scanf("%d%d",&a,&b);
            link(a,b);
        }

        changMap(n);
        init();
        int s=0 , t=2*n+1;
        //女用1~n点表示,男用n+1~n+n表示
        for(int i=1; i<=n; i++){
            addEdg(s,i,0);
            addEdg(i+n,t,0);
            for(int j=1; j<=n; j++)
            if(mapt[i][j])
                addEdg(i,j+n,1);
        }
        int l=0,r=n ,ans=0;
        while(l<=r){
            m=(l+r)>>1;
            changCap(s,t,m);
            int maxflow=maxFlow_sap(s,t,t+1);
            if(maxflow==n*m)
                ans=m , l=m+1;
            else r=m-1;
        }
        printf("%d\n",ans);
    }
}
时间: 2024-08-01 06:33:55

HDU3081Marriage Match II(二分答案+并查集+最大流SAP)经典的相关文章

HDU-3081-Marriage Match II 二分图匹配+并查集 OR 二分+最大流

二分+最大流: 1 //题目大意:有编号为1~n的女生和1~n的男生配对 2 // 3 //首先输入m组,a,b表示编号为a的女生没有和编号为b的男生吵过架 4 // 5 //然后输入f组,c,d表示编号为c的女生和编号为d的女生是朋友 6 // 7 //进行配对的要求满足其一即可. 8 //1.a女生没有和b男生吵过架 9 //2.a女生的朋友和b男生没有吵过架 10 // 11 //每进行一轮之后重新配对,配过得一对不可再配,问最多能进行几轮. 12 // 13 //题解: 14 //这一道

HDU 3081 Marriage Match II(网络流+并查集+二分答案)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3081 题目: 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. W

HDU-3081-Marriage Match 2(最大流, 二分答案, 并查集)

链接: https://vjudge.net/problem/HDU-3081 题意: 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 frie

BZOJ 1196 [HNOI2006]公路修建问题(二分答案+并查集)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1196 [题目大意] 对于每条可能维修的公路可选择修一级公路或者二级公路,价值不同 要求图连通,且至少有k条一级公路时最大价值公路价值最小. [题解] 二分答案,从一级公路开始处理,利用并查集验证两个条件. [代码] #include <cstdio> #include <algorithm> using namespace std; const int N=20005;

BZOJ 1196 二分答案+并查集

http://www.lydsy.com/JudgeOnline/problem.php?id=1196 题目大意:n个城市,m-1条路,每条路有一级公路和二级公路之分,你要造n-1条路,一级公路至少要造k条,求出所造路的最大所需的val的最小值. 思路:首先我们一定要明确这个不是一题求所有花费的最小值的问题.然后我们只要二分答案就可以了.最后注意一下条件的拜访即可. //看看会不会爆int!数组会不会少了一维! //取物问题一定要小心先手胜利的条件 #include <bits/stdc++.

BZOJ 2654 tree(二分答案+并查集)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2654 [题目大意] 给你一个无向带权连通图,每条边是黑色或白色. 让你求一棵最小权的恰好有need条白色边的生成树.题目保证有解. [题解] 我们发现对于选中的边白色是从小到大的,黑色也是从小到大的, 因此我们对所有的白色边加一个权值,那么排序后做mst选取的白色边数量增减性单调, 对于增加的权值进行二分,验证能否满足要求即可. [代码] #include <cstdio> #in

【bzoj3007】拯救小云公主 二分+对偶图+并查集

题目描述 英雄又即将踏上拯救公主的道路…… 这次的拯救目标是——爱和正义的小云公主. 英雄来到boss的洞穴门口,他一下子就懵了,因为面前不只是一只boss,而是上千只boss.当英雄意识到自己还是等级1的时候,他明白这就是一个不可能完成的任务. 但他不死心,他在想,能不能避开boss去拯救公主呢,嘻嘻. Boss的洞穴可以看成一个矩形,英雄在左下角(1,1),公主在右上角(row,line).英雄为了避开boss,当然是离boss距离越远越好了,所以英雄决定找一条路径使到距离boss的最短距离

洛谷P1182 数列分段Section II 二分答案

洛谷P1182 数列分段Section II 二分答案 题意:将 n 个 数 分为 m段 求一种方案,使这m段中最大的和 最小 额..可能有点拗口,其实就是说每一种方案,都有对应的 每段和的最大值,要求一种方案,使最大值最小 题解 :二分答案 mid为分成的最大值, 然后O(n) 判断 答案 是否可行 贪心的做下去,如果再加上就要超过了,那就新开一段 最后判断开的段数是否小于 m 1.注意要判断 如果当前这个值大于 mid,一个值就已经大于 mid了,那就直接退出了,否则 ,这个值也只会单独算为

HDU 3081 Marriage Match II 二分+最大流

题目来源:HDU 3081 Marriage Match II 题意: 思路: 错误代码 纠结不知道哪错了 先放一放 #include <cstdio> #include <queue> #include <vector> #include <cstring> #include <algorithm> using namespace std; const int maxn = 1010; const int INF = 999999999; st