【网络流】hdu3081 Marriage Match II

题意:有2*n个孩子,n个男孩编号1到n,n个女孩编号1到n。女士优先,所以每一女孩可以先选择没有和她产生过矛盾的男孩来组建一个家庭。除此之外,女孩X还可以选择男孩Z入股女孩X的朋友女孩Y没有和男孩Z发生过矛盾。如果a和b是朋友,b和c是朋友,那么a和c也是朋友。每次女孩们选好了男朋友她们都会进行一轮“婚姻匹配”。每一轮结束后,每个女孩都会在她之前没有选择的男孩里面选择一个新的男朋友,下一轮就又开始了。问总共能玩几轮?
难度:3
题解:二分+并查集+最大流。新增一个附加源s和一个附加汇t,女生向所有和她所在的并查集女生没有矛盾的男生连一条容量为1的边,每次从s向每个女生连容量为mid的边,从每个男生向t连容量为mid的边,二分枚举mid(0<=mid<=n),找到最大的mid满足求得的最大流是mid*n。
#include <cstdio>
#include <cstring>
using namespace std;
const int mm=222222;
const int mn=222;
const int oo=1000000000;
int node,src,dest,edge;
int ver[mm],flow[mm],next[mm];
int head[mn],work[mn],dis[mn],q[mn];
inline int min(int a,int b) {
    return a<b?a:b;    
}
inline void prepare(int _node,int _src,int _dest) {
    node=_node,src=_src,dest=_dest;
    for(int i=0;i<node;i++) head[i]=-1;
    edge = 0;    
}
inline void addedge(int u,int v,int c) {
    ver[edge]=v,flow[edge]=c,next[edge]=head[u],head[u]=edge++;
    ver[edge]=u,flow[edge]=0,next[edge]=head[v],head[v]=edge++;    
}
bool Dinic_bfs() {
    int i,u,v,l,r=0;
    for(i=0;i<node;++i) dis[i]=-1;
    dis[q[r++]=src]=0;
    for(l=0;l<r;++l) 
        for(i=head[u=q[l]];i>=0;i=next[i]) 
            if(flow[i]&&dis[v=ver[i]]<0) {
                dis[q[r++]=v]=dis[u]+1;
                if(v==dest) return 1;    
            }    
    return 0;
}
int Dinic_dfs(int u,int exp) {
    if(u==dest) return exp;
    for(int &i=work[u],v,tmp;i>=0;i=next[i])
        if(flow[i]&&dis[v=ver[i]]==dis[u]+1&&(tmp=Dinic_dfs(v,min(exp,flow[i])))>0) {
            flow[i]-=tmp;
            flow[i^1]+=tmp;
            return tmp;    
        }   
    return 0;
}
int Dinic_flow() {
    int i,ret=0,delta;
    while(Dinic_bfs()) {
        for(i=0;i<node;++i) work[i]=head[i];
        while(delta=Dinic_dfs(src,oo)) ret+=delta;    
    }    
    return ret;
}
int fa[mn];
int find(int x) {
    return x == fa[x] ? x : fa[x] = find(fa[x]);    
}
void Union(int x,int y) {
    int a = find(x) , b = find(y);
    fa[a] = fa[b] = fa[x] = fa[y] = min(a,b);    
}
int girl[mn*mn] , boy[mn*mn];
int n,m,f,T;
bool match[111][111];
bool check(int mid) {
    prepare(2*n+2,0,2*n+1);
    for(int i=1;i<=n;i++) {
        addedge(src,i,mid);
        addedge(i+n,dest,mid);    
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(match[find(i)][j])
                addedge(i,j+n,1);
    if(Dinic_flow() == n*mid) return 1;
    return 0;
}
int main() {
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d%d",&n,&m,&f);
        for(int i=1;i<=n;i++) fa[i] = i;
        for(int i=0;i<m;i++) 
            scanf("%d%d",&girl[i],&boy[i]);
        for(int i=0;i<f;i++) {
            int u,v;
            scanf("%d%d",&u,&v);
            Union(u,v);    
        }    
        memset(match,0,sizeof(match));
        for(int i=0;i<m;i++) {
            int u=find(girl[i]);
            match[u][boy[i]] = 1;    
        }
        int left = 0 , right = n;
        int ans = -1;
        while(left <= right) {
            int mid = (left + right) >> 1;
            if(check(mid)) {
                ans = mid;
                left = mid + 1;
            }    
            else right = mid - 1;    
        }
        printf("%d\n",ans);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值