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\n",low);
161     }
162 }

猜你喜欢

转载自www.cnblogs.com/computer-luo/p/9765698.html
今日推荐