浏览器标题切换
浏览器标题切换end

HDU3277-Marriage Match III-并查集+二分+最大流

题意:

n个女孩要与n个男孩玩配对游戏.每个女孩有一个可选男孩的集合(即该女孩可以选自己喜欢集合中的任意一个男孩作为该轮的搭档),还能选自己喜欢的男孩外还能选任意K个自己不喜欢的男孩.

然后从第一轮开始,每个女孩都要和一个不同的男孩配对.如果第一轮n个女孩都配对成功,那么就开始第二轮配对,女孩依然从自己的备选男孩集合中选择,但是不能选那些已经被该女孩在前几轮选择中选过的男孩了。问游戏最多能进行多少轮?

 

思路:

给出n,m,k,f,之后会给出m行x、y,代表编号x的girl可以和编号为y的男孩配对--------->>用ee数组做标记,代表x可以和y配对

f 行x、y,表示女孩子x和女孩子y是好朋友。因为女孩是抱团的,即女孩a如果和女孩b是好朋友,且女孩a可以和男孩a配对,那么女孩b也可以和男孩a配对----------->>所以利用并查集将是好朋友的女生合并

这道题目在Marriage Match II的基础上增添了一个条件:女孩子还可以选任意K个自己不喜欢的男孩,

之前Marriage Match II可以用匈牙利算法做,但是这道题目需要建图

防止超时,所以二分枚举答案,

源点s编号0,女孩i分成两个点i和i+n编号(编号i的点用来连接该女孩喜欢的男孩,编号为i+n的点用来连接该女孩不喜欢的男孩), 男孩编号为2n+1到2n+n, 汇点t编号为3n+1.

首先源点s到第i个女孩有边(s, i, mid)

第i个女孩的i点到i+n点有边(i, i+n, k)

如果第i个女孩可以选男孩j,那么有边(i, j, 1). 否则有边(i+n, j, 1)

每个男孩j到汇点t有边(j, t, mid)

最后看所求的dinic最大流是否等于mid*n即可。

e[i][j]==1 表示第i个女孩可以选第j个男孩. 这里的可选指的是女孩没和男孩吵架或女孩的朋友没和男孩吵架.)

 

强调!!!

  1. 一定要初始化一开始,因为并查集的原因,否则结果还是不对;
  2. 之后建图枚举答案的时候,需要改变一点点dinic的模板,因为是枚举答案,每次的枚举都会在原有的建图上进行add添边从而造成结果错误,正确做法应该是在每次枚举的时候都清空,即重新建图添边。

 

 

AC代码:

  1 #include<stdio.h>
  2 #include<iostream>
  3 #include<queue>
  4 #include<string.h>
  5 using namespace std;
  6 #define inf 0x3f3f3f3f
  7 
  8 const int N=300;
  9 int n,s,t,k,tot,f[N],ee[N][N];
 10 int head[10*N],dep[10*N],cur[10*N];
 11 struct node
 12 {
 13     int nextt,v,flow;
 14 } e[N*N];
 15 
 16 int getf(int x)
 17 {
 18     if(f[x]==x)
 19         return x;
 20     return f[x]=getf(f[x]);
 21 }
 22 
 23 void merge(int x,int y)
 24 {
 25     int t1=getf(x);
 26     int t2=getf(y);
 27     f[t2]=t1;
 28 }
 29 
 30 struct Dinic
 31 {
 32     void init()
 33     {
 34         tot=-1,s=0,t=n*3+1;
 35         for(int i=1; i<=n; i++)
 36             f[i]=i;
 37         memset(head,-1,sizeof(head));
 38     }
 39 
 40     void add(int u,int v,int flow)
 41     {
 42         tot++;
 43         e[tot].nextt=head[u];
 44         head[u]=tot;
 45         e[tot].v=v;
 46         e[tot].flow=flow;
 47 
 48         tot++;
 49         e[tot].nextt=head[v];
 50         head[v]=tot;
 51         e[tot].v=u;
 52         e[tot].flow=0;
 53     }
 54 
 55     int dfs(int u,int flow)
 56     {
 57         if(u==t)
 58             return flow;
 59         for(int &i=cur[u]; i!=-1; i=e[i].nextt) //注意这里的&符号,这样i增加的同时也能改变cur[u]的值,达到记录当前弧的目的
 60         {
 61             if((dep[e[i].v]==dep[u]+1)&&e[i].flow>0)
 62             {
 63                 int di=dfs(e[i].v,min(flow,e[i].flow));
 64                 if(di>0)
 65                 {
 66                     e[i].flow-=di;
 67                     e[i^1].flow+=di;
 68                     return di;
 69                 }
 70             }
 71         }
 72         return 0;
 73     }
 74 
 75     bool bfs()
 76     {
 77         if(s==t)return 0;
 78         queue<int>Q;
 79         while(!Q.empty())
 80             Q.pop();
 81         memset(dep,-1,sizeof(dep));
 82         dep[s]=1;
 83         Q.push(s);
 84         while(!Q.empty())
 85         {
 86             int u=Q.front();
 87             Q.pop();
 88             for (int i=head[u]; i!=-1; i=e[i].nextt)
 89             {
 90                 if ((e[i].flow>0)&&(dep[e[i].v]==-1))
 91                 {
 92                     dep[e[i].v]=dep[u]+1;
 93                     Q.push(e[i].v);
 94                 }
 95             }
 96         }
 97         if(dep[t]!=-1)
 98             return 1;
 99         return 0;
100     }
101 
102     int dinicc()
103     {
104         int sum=0;
105         while(bfs())
106         {
107             for(int i=s; i<=t; i++)
108                 cur[i]=head[i];
109             int di;
110             while((di=dfs(s,inf)))
111                 sum+=di;
112         }
113         return sum;
114     }
115 } dinic;
116 
117 
118 bool solve(int x)
119 {
120     dinic.init();
121     for(int i=1; i<=n; i++)
122     {
123         dinic.add(s,i,x);
124         dinic.add(i,i+n,k);
125         dinic.add(2*n+i,t,x);
126     }
127     for(int i=1; i<=n; i++)
128     {
129         for(int j=1; j<=n; j++)
130         {
131             if(ee[i][j])
132                 dinic.add(i,2*n+j,1);
133             else
134                 dinic.add(i+n,2*n+j,1);
135         }
136     }
137     if(dinic.dinicc()==n*x)
138         return 1;
139     return 0;
140 }
141 
142 int main()
143 {
144     int m,F,T;
145     scanf("%d",&T);
146     while(T--)
147     {
148         scanf("%d %d %d %d",&n,&m,&k,&F);
149 //        init();
150 //        dinic.init();
151         for(int i=1; i<=n; i++)
152             f[i]=i;
153         memset(ee,0,sizeof(ee));
154         for(int i=1; i<=m; i++) //girl and boy never quarrel
155         {
156             int x,y;
157             scanf("%d %d",&x,&y);
158             ee[x][y]=1;
159         }
160         for(int i=1; i<=F; i++)//x and y are friends
161         {
162             int x,y;
163             scanf("%d %d",&x,&y);
164             merge(x,y);
165         }
166         for(int i=1; i<=n; i++)
167         {
168             for(int j=i+1; j<=n; j++)
169             {
170                 if(getf(i)==getf(j))
171                 {
172                     for(int kk=1; kk<=n; kk++)
173                     {
174                         if(ee[i][kk]||ee[j][kk])
175                             ee[i][kk]=ee[j][kk]=1;
176                     }
177                 }
178             }
179         }
180         int L=0,R=n;
181         while(L<=R)
182         {
183             int mid=(L+R)>>1;
184             if(solve(mid))
185                 L=mid+1;
186             else
187                 R=mid-1;
188         }
189         printf("%d\n",L-1);
190     }
191     return 0;
192 }
View Code

 

posted @ 2020-04-14 22:18  抓水母的派大星  阅读(122)  评论(0编辑  收藏  举报