HDU 3081 Marriage Match II <<二分最大流 + 并查集
Posted computer-luo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 3081 Marriage Match II <<二分最大流 + 并查集相关的知识,希望对你有一定的参考价值。
题意
n个女孩子跟n个男孩子过家家,女孩子选男孩子,告诉你每个女孩子可选的男孩子与女孩子之间的好友关系,好友关系是互相的而且是传递的,然后如果两个女孩子是好友,他们可选的男孩子也是可以合并的。然后每一轮进行匹配,匹配成功后开始下一轮,每个女孩子只能选同一个男孩子一次,问最多能玩几轮。
思路
首先,好友关系的建立显然就直接想到了用并查集处理。
然后在建图时,可以选择是二分图,然后跑完备匹配,每次匹配完后删除匹配边进行下一次匹配。
当然,不会二分图的我就选择直接跑网络流啦,但是建图时候发现需要知道流量,这时候,就是二分答案的地方了。
具体建图:
从S向i建容量为K的边,从i向每个与他能匹配的i+n建容量为1的边,最后从i+n到T建容量为K的边
通过判断是否满流,来判断当前K是否可行,即判断maxflow==K*n
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=205; 4 const int INF=0x3f3f3f3f; 5 struct Edge{ 6 int from,to,cap,flow; 7 }; 8 struct DINIC{ 9 int n,m,s,t; 10 vector<Edge> edges; 11 vector<int> G[maxn]; 12 bool vis[maxn]; 13 int d[maxn]; 14 int cur[maxn]; 15 16 void init() 17 { 18 edges.clear(); 19 for(int i=0;i<maxn;i++) 20 G[i].clear(); 21 } 22 void AddEdge(int from,int to,int cap,int c=0) 23 { 24 edges.push_back(Edge {from,to,cap,0}); 25 edges.push_back(Edge {to,from,c,0}); 26 m=edges.size(); 27 G[from].push_back(m-2); 28 G[to].push_back(m-1); 29 } 30 31 bool BFS() 32 { 33 memset(vis,0,sizeof(vis)); 34 queue<int> Q; 35 Q.push(s); 36 d[s]=0; 37 vis[s]=1; 38 while(!Q.empty()) 39 { 40 int x=Q.front();Q.pop(); 41 for(int i=0;i<G[x].size();i++) 42 { 43 Edge& e=edges[G[x][i]]; 44 if(!vis[e.to]&&e.cap>e.flow) 45 { 46 vis[e.to]=1; 47 d[e.to]=d[x]+1; 48 Q.push(e.to); 49 } 50 } 51 } 52 return vis[t]; 53 } 54 55 int DFS(int x,int a) 56 { 57 if(x==t||a==0) return a; 58 int flow=0,f; 59 for(int& i=cur[x];i<G[x].size();i++) 60 { 61 Edge& e=edges[G[x][i]]; 62 if(d[x]+1==d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0) 63 { 64 e.flow+=f; 65 edges[G[x][i]^1].flow-=f; 66 flow+=f; 67 a-=f; 68 if(a==0) break; 69 } 70 } 71 return flow; 72 } 73 int Maxflow(int s,int t) 74 { 75 this->s=s;this->t=t; 76 int flow=0; 77 while(BFS()) 78 { 79 memset(cur,0,sizeof(cur)); 80 flow+=DFS(s,INF); 81 } 82 return flow; 83 } 84 }ANS; 85 int N,F,M,SS,TT; 86 int far[105]; 87 bool dist[maxn][maxn]; 88 void init() 89 { 90 memset(dist,0,sizeof(dist)); 91 for(int i=0;i<=N;i++) 92 { 93 far[i]=i; 94 } 95 } 96 int Find(int x) 97 { 98 if(far[x]==x) return x; 99 else return far[x]=Find(far[x]); 100 } 101 void Union(int x,int y) 102 { 103 x=Find(x),y=Find(y); 104 if(x!=y) far[y]=x; 105 } 106 void build(int now) 107 { 108 ANS.init(); 109 for(int i=1;i<=N;i++) 110 { 111 ANS.AddEdge(SS,i,now); 112 for(int j=N+1;j<=N*2;j++) 113 if(dist[i][j]) ANS.AddEdge(i,j,1); 114 } 115 for(int i=N+1;i<=2*N;i++) 116 ANS.AddEdge(i,TT,now); 117 } 118 bool check(int now) 119 { 120 build(now); 121 return ANS.Maxflow(SS,TT)==N*now; 122 } 123 int main() 124 { 125 int T; 126 scanf("%d",&T); 127 while(T--) 128 { 129 scanf("%d%d%d",&N,&M,&F); 130 SS=0,TT=2*N+1; 131 init(); 132 int low=0,high=N+1; 133 for(int i=0,u,v;i<M;i++) 134 { 135 scanf("%d%d",&u,&v); 136 dist[u][v+N]=1; 137 } 138 for(int i=1,u,v;i<=F;i++)//并查集操作 139 { 140 scanf("%d%d",&u,&v); 141 Union(u,v); 142 } 143 for(int i=1;i<=N;i++)//合并共用边,这里是抄网上大佬的 144 { 145 for(int j=i+1;j<=N;j++) 146 { 147 if(Find(i)==Find(j)) 148 { 149 for(int k=N+1;k<=N*2;k++) 150 dist[i][k]=dist[j][k]=(dist[i][k]||dist[j][k]); 151 } 152 } 153 } 154 while(high-low>1)//二分 155 { 156 int mid=(low+high)>>1; 157 if(check(mid)) low=mid; 158 else high=mid; 159 } 160 printf("%d ",low); 161 } 162 }
以上是关于HDU 3081 Marriage Match II <<二分最大流 + 并查集的主要内容,如果未能解决你的问题,请参考以下文章
HDU-3081-Marriage Match 2(最大流, 二分答案, 并查集)
HDU 3081 Marriage Match II 二分图最大匹配
HDU 3081 Marriage Match II(网络流+并查集+二分答案)
HDU 3081 Marriage Match II 二分 + 网络流