码迷,mamicode.com
首页 > 其他好文 > 详细

hdu 3081(二分+并查集+最大流||二分图匹配)

时间:2016-07-31 18:57:09      阅读:220      评论:0      收藏:0      [点我收藏+]

标签:

Marriage Match II

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


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
 

 

Source
 
题意:n个男生,n个女生,接下来有 m个关系,u v表示第 u 个女生和第 v个男生可以配对,然后接下来有 f 个关系,u v表示第 u个女生和第v个女生是好友,如果 u 和 v可以配对,u 和 w是好友,那么w 和 v也是可以配对的.问知道这些人的关系,最多可以完全配对多少次(完全配对是指n个男生和n个女生都可以配对)?
题解1(网络流):假设每个男(女)生最多可以配对k次,那么我们就从源点向每个女生连一条容量为k的边,男生向汇点也是。然后将m个关系的男女生连容量为1的边,但是,还有一个最难弄得地方,怎么让女生的朋友和其能够配对的男生连边??这时候就要用到并查集了,为了防止重连,我们连的时候要判断一下这个女生的朋友是否已经和该男生配过对了。至于k,我们可以二分枚举,如果每次都是满流,那么这个容量就是可以的。(个人推荐解法二)
#include<iostream>
#include<cstdio>
#include<cstring>
#include <algorithm>
#include <math.h>
#include <queue>
using namespace std;
const int N = 205;
const int INF = 999999999;
struct Edge{
    int v,w,next;
}edge[N*N];
int head[N];
int level[N];
int tot,max_increase;
void init()
{
    memset(head,-1,sizeof(head));
    tot=0;
}
void addEdge(int u,int v,int w,int &k)
{
    edge[k].v = v,edge[k].w=w,edge[k].next=head[u],head[u]=k++;
    edge[k].v = u,edge[k].w=0,edge[k].next=head[v],head[v]=k++;
}
int BFS(int src,int des)
{
    queue<int>q;
    memset(level,0,sizeof(level));
    level[src]=1;
    q.push(src);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        if(u==des) return 1;
        for(int k = head[u]; k!=-1; k=edge[k].next)
        {
            int v = edge[k].v;
            int w = edge[k].w;
            if(level[v]==0&&w!=0)
            {
                level[v]=level[u]+1;
                q.push(v);
            }
        }
    }
    return -1;
}
int dfs(int u,int des,int increaseRoad){
    if(u==des||increaseRoad==0) {
        return increaseRoad;
    }
    int ret=0;
    for(int k=head[u];k!=-1;k=edge[k].next){
        int v = edge[k].v,w=edge[k].w;
        if(level[v]==level[u]+1&&w!=0){
            int MIN = min(increaseRoad-ret,w);
            w = dfs(v,des,MIN);
            if(w > 0)
            {
                edge[k].w -=w;
                edge[k^1].w+=w;
                ret+=w;
                if(ret==increaseRoad){
                    return ret;
                }
            }
            else level[v] = -1;
            if(increaseRoad==0) break;
        }
    }
    if(ret==0) level[u]=-1;
    return ret;
}
int Dinic(int src,int des)
{
    int ans = 0;
    while(BFS(src,des)!=-1) ans+=dfs(src,des,INF);
    return ans;
}
bool vis[N][N],vis1[N][N];
int n,m,f;
int father[N];
int _find(int u){
    if(father[u]!=u){
        father[u] = _find(father[u]);
    }
    return father[u];
}
void build(int c){
    init();
    for(int i=1;i<=n;i++){
        for(int j=1+n;j<=n+n;j++){
            vis[i][j] = vis1[i][j];
        }
    }
    int src = 0,des = 2*n+1;
    for(int i=1;i<=n;i++){
        addEdge(src,i,c,tot);
        addEdge(i+n,des,c,tot);
    }
    for(int i=1;i<=n;i++){
        for(int j=1+n;j<=n+n;j++){
            if(vis[i][j]) addEdge(i,j,1,tot);
        }
    }
    /**本题难点*/
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(_find(i)==_find(j)){
                for(int k=1+n;k<=2*n;k++){
                    if(vis[i][k]&&!vis[j][k]){
                        addEdge(j,k,1,tot);
                        vis[j][k] = 1;
                    }
                }
            }
        }
    }
}
int main()
{
    int tcase;
    scanf("%d",&tcase);
    while(tcase--){
        scanf("%d%d%d",&n,&m,&f);
        for(int i=1;i<=n;i++) father[i] = i;
        memset(vis,false,sizeof(vis));
        memset(vis1,false,sizeof(vis1));
        for(int i=1;i<=m;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            v+=n;
            vis[u][v] = vis1[u][v] = true;
        }
        for(int i=1;i<=f;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            int a = _find(u),b = _find(v);
            father[a] = b;
        }
        int l =0,r = n,ans = 0;
        while(l<=r){
            int mid = (l+r)>>1;
            build(mid);
            if(Dinic(0,2*n+1)==n*mid){
                ans = mid;
                l = mid+1;
            }else r = mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

 题解二:二分图,建好边之后每次匹配完如果最大匹配还是n的话就删完匹配边继续进行下一次匹配,知道最大匹配<n.难点还是在于建图.

#include<iostream>
#include<cstdio>
#include<cstring>
#include <algorithm>
#include <math.h>
#include <queue>
using namespace std;
const int N = 205;
int graph[N][N];
int linker[N];
bool vis[N];
int father[N];
int n,m,f;
int _find(int u){
    if(father[u]!=u){
        father[u] = _find(father[u]);
    }
    return father[u];
}
bool dfs(int u){
    for(int v=1;v<=n;v++){
        if(graph[u][v]&&!vis[v]){
            vis[v] = true;
            if(linker[v]==-1||dfs(linker[v])){
                linker[v] = u;
                return true;
            }
        }
    }
    return false;
}
int main()
{
    int tcase;
    scanf("%d",&tcase);
    while(tcase--){
        scanf("%d%d%d",&n,&m,&f);
        for(int i=1;i<=n;i++) father[i] = i;
        memset(graph,0,sizeof(graph));
        for(int i=1;i<=m;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            graph[u][v] = 1;
        }
        for(int i=1;i<=f;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            int a = _find(u),b = _find(v);
            father[a] = b;
        }
        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(graph[i][k]) graph[j][k] = 1;
                    }
                }
            }
        }
        int ans = 0;
        while(1){
            int res = 0;
            memset(linker,-1,sizeof(linker));
            for(int i=1;i<=n;i++){
                memset(vis,false,sizeof(vis));
                if(dfs(i)) res++;
            }
           if(res<n) break;
           ans++;
           for(int i=1;i<=n;i++){
               if(linker[i]!=-1){
                    graph[linker[i]][i] = 0;
               }
           }
        }
        printf("%d\n",ans);
    }
    return 0;
}

总结:能用二分图就别用网络流。

hdu 3081(二分+并查集+最大流||二分图匹配)

标签:

原文地址:http://www.cnblogs.com/liyinggang/p/5723428.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!