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个男孩. 这里的可选指的是女孩没和男孩吵架或女孩的朋友没和男孩吵架.)
强调!!!
- 一定要初始化一开始,因为并查集的原因,否则结果还是不对;
- 之后建图枚举答案的时候,需要改变一点点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 }