HDU 3081 Marriage Match II (二分+并查集+最大流)

题意:N个boy和N个girl,每个女孩可以和与自己交友集合中的男生配对子;如果两个女孩是朋友,则她们可以和对方交友集合中的男生配对子;如果女生a和女生b是朋友,b和c是朋友,则a和c也是朋友.每一轮配对结束后(每个人都找到自己的对象),在开始新的一轮配对.求最大能进行多少轮完整的游戏.
分析:用最大流解决该问题,若假设能进行k轮游戏,则由源点向每个女生建一条容量为k的边,每个男生也向汇点建一条容量为k的边,对于每个女生,向其能够配对的男生连一条容量为1的边.最后跑出最大流若为n*k则表示游戏能够进行k轮.
确定了这点之后,二分搜答案.
还有一个问题就是,如何通过伙伴关系来确定女生能配对的所有男生? 有并查集和传递闭包两种方法,复杂度是差不多.属于一个集合中的女生,则她们的匹配对象集合就是所有人对象集合的并集.

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN=10010;//点数的最大值
const int MAXM=400010;//边数的最大值
#define captype int

struct SAP_MaxFlow{
    struct EDGE{
        int to,next;
        captype cap;
    }edg[MAXM];
    int eid,head[MAXN];
    int gap[MAXN];
    int dis[MAXN];
    int cur[MAXN];
    int pre[MAXN];

    void init(){
        eid=0;
        memset(head,-1,sizeof(head));
    }
    void AddEdge(int u,int v,captype c,captype rc=0){
        edg[eid].to=v; edg[eid].next=head[u];
        edg[eid].cap=c;  head[u]=eid++;
        edg[eid].to=u; edg[eid].next=head[v];
        edg[eid].cap=rc; head[v]=eid++;
    }
    captype maxFlow_sap(int sNode,int eNode, int n){//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 Min=INF ;
                int inser;
                for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to])
                if(Min>edg[i].cap){
                    Min=edg[i].cap;
                    inser=i;
                }
                for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to]){
                    edg[i].cap-=Min;
                    edg[i^1].cap+=Min;
                }
                ans+=Min;
                u=edg[inser^1].to;
                continue;
            }
            bool flag = false;
            int v;
            for(int i=cur[u]; i!=-1; i=edg[i].next){
                v=edg[i].to;
                if(edg[i].cap>0 && dis[u]==dis[v]+1){
                    flag=true;
                    cur[u]=pre[v]=i;
                    break;
                }
            }
            if(flag){
                u=v;
                continue;
            }
            int Mind= n;
            for(int i=head[u]; i!=-1; i=edg[i].next)
            if(edg[i].cap>0 && Mind>dis[edg[i].to]){
                Mind=dis[edg[i].to];
                cur[u]=i;
            }
            gap[dis[u]]--;
            if(gap[dis[u]]==0) return ans;
            dis[u]=Mind+1;
            gap[dis[u]]++;
            if(u!=sNode) u=edg[pre[u]^1].to;  //退一条边
        }
        return ans;
    }
}F;

bool G[205][205];
int fa[105];

int Find(int x){
    return fa[x]==-1? x:fa[x] = Find(fa[x]);
}

void Union(int x,int y)
{
    x = Find(x), y = Find(y);
    if(x!=y) fa[x] = y;
}

bool check(int n,int limit){
    F.init();
    int st =0,ed = 2*n+1;
    for(int i=1;i<=n;++i){
        for(int j = n+1;j<=2*n;++j){
            if(G[i][j]) F.AddEdge(i,j,1);
        }
    }
    for(int i=1;i<=n;++i) F.AddEdge(st,i,limit);
    for(int i=n+1;i<=2*n;++i) F.AddEdge(i,ed,limit);
    int res = F.maxFlow_sap(st,ed,2*n+2);
    return res == n*limit;
}

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    int T; scanf("%d",&T);
    int n,m,f,u,v,tmp;
    while(T--){
        scanf("%d %d %d",&n,&m,&f);
        memset(G,0,sizeof(G));
        memset(fa,-1,sizeof(fa));
        for(int i =1;i<=m;++i){
            scanf("%d %d",&u,&v);
            G[u][v+n] = 1;
        }
        for(int i=1;i<=f;++i){
            scanf("%d %d",&u,&v);
            Union(u,v);
        }
        for(int i=1;i<=n;++i){
            for(int j=i+1;j<=n;++j){
                if(Find(i)==Find(j)){
                    for(int k=n+1;k<=2*n;++k){
                        G[i][k] = G[j][k] = (G[i][k] || G[j][k]);
                    }
                }
            }
        }
        int L = 0,R =100,mid,ans=0;
        while(L<=R){
            mid  =(L+R)>>1;
            if(check(n,mid)){
                ans = mid;
                L=  mid+1;
            }
            else {
                R=  mid-1;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
posted @ 2018-09-07 21:27  xiuwenL  阅读(97)  评论(0编辑  收藏  举报