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

Posted kongbursi-2292702937

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 //这一道题要二分答案,然后用最大流来跑
 15 //可能很疑惑最大流怎么去跑这个题目,我们可以假设可以进行mid轮,然后我们从起点给女生连一条容量为mid的
 16 //边,然后终点也和男孩之间连一条容量为mid的边。之后如果男孩和女孩之间可以组成情侣那就连一条容量为1的
 17 //边。这样的话当mid==1的时候,你跑最大流相当于每一个女孩找到了一个互不相同的男孩(因为一个男孩和终点容
 18 //量为1,对吧!!),当mid>1的时候,这个时候对于一个男孩可以被选择mid次(保证这个mid是可行解),因为每一
 19 //个女生和一个男生的边容量只是1,所以一个女生不可能选择多次是同一个男生。那么对于所有男生都会有mid个
 20 //女生选择他们,那么这样的话就刚好可以凑成mid轮
 21 
 22 #include<stdio.h>
 23 #include<string.h>
 24 #include<iostream>
 25 #include<algorithm>
 26 #include<queue>
 27 using namespace std;
 28 const int maxn=1010;
 29 const int INF=0x3f3f3f3f;
 30 int head[maxn],cnt,st,en,dis[maxn],cur[maxn],w[105][105],q[105][105];
 31 struct edge
 32 {
 33     int v,next,c,flow;
 34 } e[100000];
 35 void add_edge(int x,int y,int z)
 36 {
 37     e[cnt].v=y;
 38     e[cnt].c=z;
 39     e[cnt].flow=0;
 40     e[cnt].next=head[x];
 41     head[x]=cnt++;
 42 
 43     e[cnt].v=x;
 44     e[cnt].c=0;
 45     e[cnt].flow=0;
 46     e[cnt].next=head[y];
 47     head[y]=cnt++;
 48 }
 49 bool bfs()
 50 {
 51     memset(dis,0,sizeof(dis));
 52     dis[st]=1;
 53     queue<int>r;
 54     r.push(st);
 55     while(!r.empty())
 56     {
 57         int x=r.front();
 58         r.pop();
 59         for(int i=head[x]; i!=-1; i=e[i].next)
 60         {
 61             int v=e[i].v;
 62             if(!dis[v] && e[i].c>e[i].flow)
 63             {
 64                 dis[v]=dis[x]+1;
 65                 r.push(v);
 66             }
 67         }
 68     }
 69     return dis[en];
 70 }
 71 int dinic(int s,int limit)
 72 {
 73     if(s==en || !limit) return limit;
 74     int ans=0;
 75     for(int &i=cur[s]; i!=-1; i=e[i].next)
 76     {
 77         int v=e[i].v,feed;
 78         if(dis[v]!=dis[s]+1) continue;
 79         feed=dinic(v,min(limit,e[i].c-e[i].flow));
 80         if(feed)
 81         {
 82             e[i].flow+=feed;
 83             e[i^1].flow-=feed;
 84             limit-=feed;
 85             ans+=feed;
 86             if(limit==0) break;
 87         }
 88     }
 89     if(!ans) dis[s]=-1;
 90     return ans;
 91 }
 92 int main()
 93 {
 94     int t;
 95     scanf("%d",&t);
 96     while(t--)
 97     {
 98         memset(w,0,sizeof(w));
 99         memset(q,0,sizeof(q));
100         st=0;
101         int n,m,f,x,y;
102         scanf("%d%d%d",&n,&m,&f);
103         en=2*n+1;
104         for(int i=1; i<=m; ++i)
105         {
106             scanf("%d%d",&x,&y);
107             q[x][y]=1;
108         }
109         for(int i=1; i<=f; ++i)
110         {
111             scanf("%d%d",&x,&y);
112             w[x][y]=w[y][x]=1;
113         }
114 
115         int l=0,r=n,mid,flag=0,sum;
116         while(l<=r)
117         {
118             mid=(l+r)>>1;
119             memset(head,-1,sizeof(head));
120             cnt=0;
121             for(int i=1; i<=n; ++i)
122             {
123                 add_edge(st,i,mid);
124                 add_edge(i+n,en,mid);
125             }
126             for(int i=1; i<=n; ++i)
127             {
128                 for(int j=1; j<=n; ++j)
129                 {
130                     if(i==j) continue;
131                     if(w[i][j])
132                     {
133                         for(int k=1; k<=n; ++k)
134                         {
135                             if(q[j][k])
136                             {
137                                 q[i][k]=1;
138                             }
139                         }
140                     }
141                 }
142             }
143             for(int i=1; i<=n; ++i)
144             {
145                 for(int j=1; j<=n; ++j)
146                 {
147                     if(q[i][j])
148                         add_edge(i,j+n,1);
149                 }
150             }
151             int ans=0;
152             while(bfs())
153             {
154                 for(int i=0; i<=en; i++)
155                     cur[i]=head[i];
156                 ans+=dinic(st,INF); //这里原来是1我改成了INF,是这里的错.变成1的话最大流算法跑了好多次,就会TLE
157             }
158             if(ans>=n*mid)   
159             {
160                 sum=mid;
161                 l=mid+1;
162             }
163             else r=mid-1;
164         }
165 
166         printf("%d
",sum);
167 
168     }
169     return 0;
170 }

二分匹配:

  1 //还可以用二分图匹配+并查集来写(我是用的最大流,以下代码转自:https://blog.csdn.net/loy_184548/article/details/51461601)
  2 //
  3 //题目大意:有编号为1~n的女生和1~n的男生配对
  4 //首先输入m组,a,b表示编号为a的女生没有和编号为b的男生吵过架
  5 //
  6 //然后输入f组,c,d表示编号为c的女生和编号为d的女生是朋友
  7 //
  8 //进行配对的要求满足其一即可。 
  9 //1.a女生没有和b男生吵过架 
 10 //2.a女生的朋友和b男生没有吵过架
 11 //
 12 //每进行一轮之后重新配对,配过得一对不可再配,问最多能进行几轮。
 13 //
 14 //题目思路:
 15 //第一步:要求2可以处理一下变成要求1.这里就需要用到并查集啦(刚开始用dfs处理,然而超内存了)
 16 //1.两个女生是朋友就用并查集合并 
 17 //2.遍历,如果两个女生a,b父节点一样,那么b所能配对的男生a也能配对
 18 
 19 //  链接:https://blog.csdn.net/loy_184548/article/details/51461601
 20 //  HDU-3081.cpp
 21 //  HDU
 22 //
 23 //  Created by pro on 16/5/20.
 24 //  Copyright (c) 2016年 loy. All rights reserved.
 25 //
 26 
 27 #include <iostream>
 28 #include <cstdio>
 29 #include <cmath>
 30 #include <vector>
 31 #include <cstring>
 32 #include <algorithm>
 33 #include <string>
 34 #include <set>
 35 #include <functional>
 36 #include <numeric>
 37 #include <sstream>
 38 #include <stack>
 39 #include <map>
 40 #include <queue>
 41 #include<iomanip>
 42 using namespace std;
 43 int g[105][105];
 44 #define MAXN 105
 45 int fa[MAXN] = {0};
 46 int vis[105];
 47 int link[105];  //编号为i的女生对应的男生编号
 48 int n,m,f,ans = 0;
 49 void initialise(int n)          //初始化
 50 {
 51     for (int i = 1; i <= n; i++)
 52         fa[i] = i;
 53 }
 54 int getfather(int v)            //父节点
 55 {
 56     return (fa[v] == v) ? v : fa[v] = getfather(fa[v]);
 57 }
 58 void merge(int x,int y)         //合并
 59 {
 60     x = getfather(x);
 61     y = getfather(y);
 62     if (x != y)
 63         fa[x] = y;
 64 }
 65 
 66 bool dfs(int u) {
 67     for (int v = 1; v <= n; v++) {
 68         if (!vis[v] && g[u][v]) {
 69             vis[v] = 1;
 70             if (!link[v] || dfs(link[v])) {
 71                 link[v] = u;
 72                 return true;
 73             }
 74         }
 75     }
 76     return false;
 77 }
 78 void solve()
 79 {
 80     while(1)
 81     {
 82        // cout << ans << endl;
 83         memset(link,0,sizeof(link));
 84         int cnt = 0;
 85         for (int i = 1; i <= n; i++)
 86         {
 87             memset(vis,0,sizeof(vis));
 88             if (dfs(i)) cnt++;  //如果找到了能配对的
 89         }
 90         if (cnt == n) //如果全部配对成功
 91         {
 92             ans++;
 93             for (int i = 1; i <= n; i++)
 94             {
 95                 g[link[i]][i] = 0;   //编号为i以及对应的女生不能再连
 96             }
 97         }
 98         else
 99         {
100             break;
101         }
102     }
103 }
104 int main()
105 {
106     int t;
107     scanf("%d",&t);
108     while(t--)
109     {
110         int u,v;
111         scanf("%d%d%d",&n,&m,&f);
112         memset(g,0,sizeof(g));
113         initialise(n);
114         ans = 0;
115         for (int i = 0; i < m; i++)
116         {
117             scanf("%d%d",&u,&v);
118             g[u][v] = 1;
119         }
120         for (int i = 0; i < f; i++)
121         {
122             scanf("%d%d",&u,&v);
123             merge(u,v);
124         }
125         for (int i = 1; i <= n; i++)
126         {
127             int t = getfather(i);//得到编号为i这个女生的父节点
128 
129             for (int j = 1; j <= n; j++)
130             {
131                 if (i != j && getfather(j) == t)  //如果两个女生是朋友
132                 {
133                     for (int k = 1; k <= n; k++)  //那么j的朋友k,也是i的朋友
134                     {
135                         if (g[j][k]) g[i][k] = 1;
136                     }
137                 }
138             }
139         }
140         solve();
141         printf("%d
",ans);
142     }
143     return 0;
144 }

 

以上是关于HDU-3081-Marriage Match II 二分图匹配+并查集 OR 二分+最大流的主要内容,如果未能解决你的问题,请参考以下文章

HDU 3081 Marriage Match II 二分图最大匹配

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

HDU 3081 Marriage Match II 二分 + 网络流

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

HDU 3081:Marriage Match II(二分图匹配+并查集)

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