【hdoj 3081】Marriage Match II 【并查集orfloyd +完美匹配】or【最大流+二分+并查集】

来源:互联网 发布:c语言socket编程指南 编辑:程序博客网 时间:2024/05/29 23:43

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 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

题意 ,n个男的,m个女的,女的要和男的匹配,如果女A喜欢男A 那么女A的朋友女B也喜欢男A。 每次只有所有的人都有对象,才可以开始游戏,且每一局游戏的对象还不能和之前的一样,问最多玩几局游戏。

分析:我们可以根据女生朋友的关系来建图,然后求匹配,如果是完美匹配那么加1,然后去掉匹配的关系,再匹配,直到不完美匹配;

维护女生的关系 可以用并查集或者用floyd
代码 1 用并查集维护关系

#include<bits/stdc++.h>using namespace std;const int MAXN  =200+10;const int MAXM = 1e6;int pre[MAXN];int mp[MAXN][MAXN];int n,m,f;void init(){    for(int i=0;i<=n;i++)        pre[i]=i;    memset(mp,0,sizeof(mp));}int Find(int x){ return x==pre[x]?x:pre[x]=Find(pre[x]);}void join(int a,int b){    a=Find(a);b=Find(b);    if(a!=b) pre[a]=b;}void getmap(){    int a,b,c;    while(m--){        scanf("%d%d",&a,&b);        mp[a][b]=1;    }    while(f--){        scanf("%d%d",&a,&b);        join(a,b);    }    for(int i=1;i<=n;i++){        for(int j=1;j<=n;j++){            if(i==j) continue;            if(Find(i)==Find(j)) {                for(int k=1;k<=n;k++){                    if(mp[j][k]) mp[i][k]=1;                }            }        }    }}int pipei[MAXN],vis[MAXN];//原来我之前理解都是错的, 根据我键的图匹配出来的,//pipei[i] 。 i是男生序号 pipei[i]的值代表的是女生序号,我说去边的时候怎么老是不对。bool Findd(int x){    for(int i=1;i<=n;i++){        if(!vis[i]&&mp[x][i]){            vis[i]=1;            if(!pipei[i]||Findd(pipei[i])){                pipei[i]=x;                return 1;            }        }    }    return 0;}bool fenpei(){    int cnt=0;    memset(pipei,0,sizeof(pipei));    for(int i=1;i<=n;i++){        memset(vis,0,sizeof(vis));        if(Findd(i)) cnt++;    }    return cnt==n;}void solve(){    int cnt=0;    while(fenpei()){// 只有完美匹配        cnt++;        for(int i=1;i<=n;i++)            mp[pipei[i]][i]=0;//    }    printf("%d\n",cnt);}int main(){    int t;cin>>t;    while(t--){        scanf("%d%d%d",&n,&m,&f);        init();        getmap();        solve();    }    return 0;}

代码2 floyd 求传递闭包,来维护女生的关系 。

#include<bits/stdc++.h>using namespace std;const int MAXN  =200+10;const int MAXM = 1e6;int mp[MAXN][MAXN];int wm[MAXN][MAXN];int n,m,f;void init(){    memset(wm,0,sizeof(wm));    memset(mp,0,sizeof(mp));}void floyd(){    for(int k=1;k<=n;k++){        for(int i=1;i<=n;i++){            for(int j=1;j<=n;j++){                wm[i][j]=wm[i][j]||(wm[i][k]&&wm[k][j]);            }        }    }}void getmap(){    int a,b,c;    while(m--){        scanf("%d%d",&a,&b);        mp[a][b]=1;    }    while(f--){        scanf("%d%d",&a,&b);        wm[a][b]=wm[b][a]=1;    }    floyd();    for(int i=1;i<=n;i++){        for(int j=1;j<=n;j++){            if(i==j) continue;            if(wm[i][j]){                for(int k=1;k<=n;k++){                    if(mp[j][k]) mp[i][k]=1;                }            }        }    }}int pipei[MAXN],vis[MAXN];bool Findd(int x){    for(int i=1;i<=n;i++){        if(!vis[i]&&mp[x][i]){            vis[i]=1;            if(!pipei[i]||Findd(pipei[i])){                pipei[i]=x;                return 1;            }        }    }    return 0;}bool fenpei(){    int cnt=0;    memset(pipei,0,sizeof(pipei));    for(int i=1;i<=n;i++){        memset(vis,0,sizeof(vis));        if(Findd(i)) cnt++;    }    return cnt==n;}void solve(){    int cnt=0;    while(fenpei()){        cnt++;        for(int i=1;i<=n;i++)            mp[pipei[i]][i]=0;  //    }    printf("%d\n",cnt);}int main(){    int t;cin>>t;    while(t--){        scanf("%d%d%d",&n,&m,&f);        init();        getmap();        solve();    }    return 0;}

代码 三可以用 最大流+二分来求
此解法 原文链接
源点s为0,汇点t为2*n+1.女孩编号1到n,男孩编号n+1到2*n. 假设我们当前二分尝试的轮数为K(即能够进行K轮匹配):

首先如果女孩i可能选择男孩j,那么就有边(i, j+n, 1).且源点到每个女孩i有边(s,i,mid),每个男孩j到汇点t有边(j+n,t,mid).

如果最大流==mid*n,那么就表示可以进行最少mid轮匹配.

证明
证:如果满流,那么每个女生肯定选择了K个不同的男孩,每个男孩肯定被K个不同的女孩选择了(因为一个女孩到一个男孩边容量只为1,所以该女孩最多只能选该男孩一次).
那么上面这样就能保证这个游戏可以进行K轮吗?可以的,假设当前图的流量为0,说明任何女孩都没选男孩.
你可以想象假如此时从S到所有女孩有流量1(虽然容量是K,但是目前我们只放出1流量)流出,
那么这些流量肯定会汇集到t(因为最大流为K*n,而我们此时只不过n流量).这个汇集的过程就是第一轮女孩选择了各自不同男孩的结果.
现在从S到所有女孩又有流量1流出(即第二轮开始了),这些流量肯定又经过了n个男孩汇集到t点了 且 如果上一轮i女孩的流量走到j男孩,
这一轮i女孩的流量肯定不走j男孩了(因为i女孩到j男孩的边只有1容量).
综上所述,只要最大流==K*n,那么就能进行K轮.(如果能进行K轮配对,是不是最大流也一定==K*n呢?这个也是一定的,
也是按照上面的模型过程模拟即可.它们互为充要条件)

代码

#include<bits/stdc++.h>using namespace std;const int MAXN  =1000+10;const int MAXM = 1e5;const int inf =0x3f3f3f3f;int pre[MAXN];int mp[MAXN][MAXN];int n,m,f;void It(){    for(int i=0;i<=n;i++)        pre[i]=i;    memset(mp,0,sizeof(mp));}int Find(int x){ return x==pre[x]?x:pre[x]=Find(pre[x]);}void join(int a,int b){    a=Find(a);b=Find(b);    if(a!=b) pre[a]=b;}void Input(){    int a,b,c;    while(m--){        scanf("%d%d",&a,&b);        mp[a][b]=1;    }    while(f--){        scanf("%d%d",&a,&b);        join(a,b);    }    for(int i=1;i<=n;i++){        for(int j=1;j<=n;j++){            if(i==j) continue;            if(Find(i)==Find(j)) {                for(int k=1;k<=n;k++){                    if(mp[j][k]) mp[i][k]=1;                }            }        }    }}struct Edge {    int from,to,cap,flow,nexts;}edge[MAXM];int head[MAXN],top;void init(){    memset(head,-1,sizeof(head));    top=0;}void addedge(int a,int b,int c){    Edge e={a,b,c,0,head[a]};    edge[top]=e;head[a]=top++;    Edge ee={b,a,0,0,head[b]};    edge[top]=ee;head[b]=top++;}void getmap(int mid){    for(int i=1;i<=n;i++){            addedge(0,i,mid);            addedge(n+i,2*n+1,mid);        for(int j=1;j<=n;j++){            if(mp[i][j])                addedge(i,j+n,1);        }    }}int vis[MAXN],dis[MAXN];int cur[MAXN];bool bfs(int st,int ed){    memset(vis,0,sizeof(vis));    queue<int>Q; Q.push(st);    vis[st]=1;dis[st]=1;    while(!Q.empty()){        int now=Q.front();Q.pop();        for(int i=head[now];i!=-1;i=edge[i].nexts){            Edge e=edge[i];            if(!vis[e.to]&&e.cap-e.flow>0){                vis[e.to]=1;                dis[e.to]=dis[now]+1;                if(e.to==ed)  return 1;                Q.push(e.to);            }        }    }    return 0;}int dfs(int now,int a,int ed){    if(now==ed||a==0) return a;    int flow=0,f;    for(int& i=cur[now];i!=-1;i=edge[i].nexts){        Edge &e=edge[i];        if(dis[e.to]==dis[now]+1&&(f=dfs(e.to,min(a,e.cap-e.flow),ed))>0){            e.flow+=f;            flow+=f;            edge[i^1].flow-=f;            a-=f;            if(a==0) break;        }    }    return flow;}int max_flow(int st,int ed){    int flow=0;    while(bfs(st,ed)){        memcpy(cur,head,sizeof(head));        flow+=dfs(st,inf,ed);    }    return flow;}void solve(){   int le=0;int ri=n;   int ans=0;    while(le<=ri){        int mid=(le+ri)>>1;        init();        getmap(mid);        if(max_flow(0,n+n+1)>=mid*n) {ans=mid;le=mid+1;}        else   ri=mid-1;     }    printf("%d\n",ans);}int main(){    int t;cin>>t;    while(t--){        scanf("%d%d%d",&n,&m,&f);        It();        Input();        solve();    }    return 0;}
阅读全文
0 0
原创粉丝点击