二分匹配模板

Posted bianjunting

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二分匹配模板相关的知识,希望对你有一定的参考价值。

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <queue>
  4 #include <vector>
  5 #include <algorithm>
  6 using namespace std;
  7 /******************************************************
  8  * 二分图匹配(匈牙利算法得邻接矩阵+dfs实现)
  9  * 初始化:g[][]两边顶点的划分情况
 10  * 建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配
 11  * g 没有边相连则初始化为0
 12  * //un是匹配左边的顶点数,vn是匹配右边的顶点数
 13  * 调用:res = hungray():输出最大匹配
 14  * 优点:适用于稠密图,dfs找增广路,实现简洁易于理解
 15  * 时间复杂度:O(V,E)。
 16  * 顶点编号从0开始
 17 const int maxn = 500 + 5;
 18 int un, vn;//二分图中u, v的数目,使用前必须赋值
 19 int g[maxn][maxn];
 20 int linker[maxn];//每个结点的匹配结点
 21 bool used[maxn];         
 22 
 23 bool dfs(int u) 
 24     for(int v = 0; v < vn; v ++) 
 25         if(g[u][v] && !used[v]) 
 26             used[v] = true;
 27             if(linker[v] == -1 || dfs(linker[v])) 
 28                 linker[v] = u;
 29                 return true;
 30             
 31         
 32     
 33     return false;
 34 
 35 
 36 int hungary() 
 37     int res = 0;
 38     memset(linker, -1, sizeof linker);
 39     for(int u = 0; u < un; u ++) 
 40         memset(used, false, sizeof used);
 41         if(dfs(u)) res ++;
 42     
 43     return res;
 44 
 45  * ***************************************************/
 46 
 47 /******************************************************
 48  * 匈牙利算法链式前向星实现
 49  * 使用前init()进行初始化,给un赋值
 50  * 加边使用函数addedge(u, v)
 51 
 52 const int maxn = 5000 + 5;//点数的最大值
 53 const int maxm = 50000 + 5;//边数的最大值
 54 struct Edge 
 55     int to, next;
 56  edge[maxm];
 57 int head[maxn], tot;
 58 int linker[maxn];//每个结点的匹配结点
 59 bool used[maxn];
 60 int un;//匹配u结点的个数
 61 
 62 void init() 
 63     tot = 0;
 64     memset(head, -1, sizeof head);
 65 
 66 
 67 void addedge(int u, int v) //添加边的时候记得要让左侧匹配结点u的编号为[0, un)
 68     edge[tot].to = v; edge[tot].next = head[u];
 69     head[u] = tot ++;
 70 
 71 
 72 bool dfs(int u) 
 73     for(int i = head[u]; ~i; i = edge[i].next) 
 74         int v = edge[i].to;
 75         if(!used[v]) 
 76             used[v] = true;
 77             if(linker[v] == -1 || dfs(linker[v])) 
 78                 linker[v] = u;
 79                 return true;
 80             
 81         
 82     
 83     return false;
 84 
 85 
 86 int hungary() 
 87     int res = 0;
 88     memset(linker, -1, sizeof linker);
 89     for(int u = 0; u < un; u ++) 
 90         memset(used, false, sizeof used);
 91         if(dfs(u)) res ++;
 92     
 93     return res;
 94 
 95  * ****************************************************/
 96 
 97 /*******************************************************
 98  * 二分图匹配(Hopcroft-Karp算法)
 99  * 复杂度:O(sqrt(n) * E)
100  * 邻接表存图, vector实现
101  * vector先初始化,然后加边
102  * un为左端的顶点数,使用前赋值(点编号0开始)
103 
104 const int maxn = 3000 + 5;
105 const int inf = 0x3f3f3f3f;
106 vector <int> G[maxn];
107 int un;
108 int mx[maxn], my[maxn];
109 int dx[maxn], dy[maxn];
110 int dis;
111 bool used[maxn];
112 bool searchp() 
113     queue <int> que;
114     dis = inf;
115     memset(dx, -1, sizeof dx);
116     memset(dy, -1, sizeof dy);
117     for(int i = 0; i < un; i ++) 
118         if(mx[i] == -1) 
119             que.push(i);
120             dx[i] = 0;
121         
122     
123     while(!que.empty()) 
124         int u = que.front();
125         que.pop();
126         if(dx[u] >dis) break;
127         int sz = G[u].size();
128         for(int i = 0; i < sz; i ++) 
129             int v = G[u][i];
130             if(dy[v] == -1) 
131                 dy[v] = dx[u] + 1;
132                 if(my[v] == -1) dis = dy[v];
133                 else 
134                     dx[my[v]] = dy[v] + 1;
135                     que.push(my[v]);
136                 
137             
138         
139     
140     return dis != inf;
141 
142 
143 bool dfs(int u) 
144     int sz = G[u].size();
145     for(int i = 0; i < sz; i ++) 
146         int v = G[u][i];
147         if(!used[v] && dy[v] == dx[u] + 1) 
148             used[v] = true;
149             if(my[v] != -1 && dy[v] == dis) continue;
150             if(my[v] == -1 || dfs(my[v])) 
151                 my[v] = u;
152                 mx[u] = v;
153                 return true;
154             
155         
156     
157     return false;
158 
159 
160 int maxmatch() 
161     int res = 0;
162     memset(mx, -1, sizeof mx);
163     memset(my, -1, sizeof my);
164     while(searchp()) 
165         memset(used, false, sizeof used);
166         for(int i = 0; i < un; i ++) 
167             if(mx[i] == -1 && dfs(i))
168                 res ++;
169         
170     
171     return res;
172 
173  * ****************************************************/
174 
175 /*******************************************************
176  * 二分多重匹配
177 
178 const int maxn = 1000 + 5;
179 const int maxm = 500 + 5;
180 int un, vn;
181 int g[maxn][maxn];
182 int linker[maxm][maxn];
183 bool used[maxm];
184 int num[maxm];//右边最大的匹配数
185 
186 bool dfs(int u) 
187     for(int v = 0; v < vn; v ++) 
188         if(g[u][v] && !used[v]) 
189             used[v] = true;
190             if(linker[v][0] < num[v]) 
191                 linker[v][++ linker[v][0]] = u;
192                 return true;
193             
194             for(int i = 1; i <= num[v]; i ++) 
195                 if(dfs(linker[v][i])) 
196                     linker[v][i] = u;
197                     return true;
198                 
199             
200         
201     
202     return false;
203 
204 
205 int hungary() 
206     int res = 0;
207     for(int i = 0; i < vn; i ++)
208         linker[i][0] = 0;
209         for(int u = 0; u < un; u ++) 
210             memset(used, false, sizeof used);
211             if(dfs(u)) res ++;
212         
213     return res;
214 
215  * *****************************************************/
216 
217 /********************************************************
218  * KM算法
219  * 复杂度 O(nx * nx * ny)
220  * 求最大权匹配
221  * 若最小权匹配,可将权值取相反数,结果取相反数
222  * 点的编号从0开始
223 
224 const int maxn = 300 + 5;
225 const int inf = 0x3f3f3f3f;
226 int nx, ny;
227 int g[maxn][maxn];
228 int linker[maxn], lx[maxn], ly[maxn];// y中个点匹配状态,x, y中的点标号
229 int slack[maxn];
230 bool visx[maxn], visy[maxn];
231 
232 bool dfs(int x) 
233     visx[x] = true;
234     for(int y = 0; y < ny; y ++) 
235         if(visy[y]) continue;
236         int tmp = lx[x] + ly[y] - g[x][y];
237         if(tmp == 0) 
238             visy[y] = true;
239             if(linker[y] == -1 || dfs(linker[y])) 
240                 linker[y] = x;
241                 return true;
242             
243         
244         else if(slack[y] > tmp)
245             slack[y] = tmp;
246     
247     return false;
248 
249 
250 int km() 
251     memset(linker, -1, sizeof linker);
252     memset(ly, 0, sizeof ly);
253     for(int i = 0; i < nx; i ++) 
254         lx[i] = -inf;
255         for(int j = 0; j < ny; j ++) 
256             if(g[i][j] > lx[i])
257                 lx[i] = g[i][j];
258     
259     for(int x = 0; x < nx; x ++) 
260         for(int i = 0; i < ny; i ++)
261             slack[i] = inf;
262         while(true) 
263             memset(visx, false, sizeof visx);
264             memset(visy, false, sizeof visy);
265             if(dfs(x)) break;
266             int d = inf;
267             for(int i = 0; i < ny; i ++)
268                 if(!visy[i] && d > slack[i])
269                     d = slack[i];
270             for(int i = 0; i < nx; i ++)
271                 if(visx[i])
272                     lx[i] -= d;
273             for(int i = 0; i < ny; i ++) 
274                 if(visy[i]) ly[i] += d;
275                 else slack[i] -= d;
276             
277         
278     
279     int res = 0;
280     for(int i = 0; i < ny; i ++) 
281         if(linker[i] != -1)
282             res += g[linker[i]][i];
283     return res;
284 
285 * *****************************************************/
286 
287 /*******************************************************
288  * 最小支配集
289 const int maxn = 1000 + 5;
290 int pre[maxn];//存储父节点
291 bool visit[maxn];//DFS标记数组
292 int newpos[maxn];//遍历序列
293 int now;
294 int n, m;
295 
296 int head[maxn];//链式前向星
297 struct Node int to; int next;;
298 Node edge[maxn];
299 
300 void DFS(int x) 
301     newpos[now ++] = x;//记录遍历序列
302     for(int k = head[x]; k != -1; k = edge[k].next) 
303         if(!visit[ edge[k].to ]) 
304             visit[ edge[k].to ] = true;
305             pre[edge[k].to] = x;//记录父节点
306             DFS(edge[k].to);
307         
308     
309 
310 
311 int MDS() 
312     bool s[maxn] = 0;
313     bool set[maxn] = 0;
314     int ans = 0;
315     for(int i = n - 1; i >= 0; i--) //逆序进行贪心
316         int t = newpos[i];
317         if(!s[t])  //如果当前点没被覆盖
318             if(! set[ pre[t] ]) //当前点的父节点不属于支配集
319                 set[ pre[t] ] = true;//当前点的父节点加入支配集
320                 ans ++;  //支配集节点个数加 1
321             
322             s[t] = true; //标记当前点已被覆盖
323             s[ pre[t] ] = true;// 标记当前点的父节点被覆盖
324             s[ pre[ pre[t] ] ] = true;//标记当前点的父节点的父节点被覆盖
325         
326     
327     return ans;
328 
329 
330 void solve() 
331     memset(visit, false, sizeof(visit));//初始化
332     now = 0;
333     visit[1] = true;
334     pre[1] = 1;
335     DFS(1);//从根节点开始寻摘遍历序列
336     MDS();
337 
338  * ****************************************************/
339 
340 /*******************************************************
341  * 最小点覆盖
342 const int maxn = 1000 + 5;
343 int pre[maxn];//存储父节点
344 bool visit[maxn];//DFS标记数组
345 int newpos[maxn];//遍历序列
346 int now;
347 int n, m;
348 
349 int head[maxn];//链式前向星
350 struct Node int to; int next;;
351 Node edge[maxn];
352 
353 void DFS(int x) 
354     newpos[now ++] = x;//记录遍历序列
355     for(int k = head[x]; k != -1; k = edge[k].next) 
356         if(!visit[ edge[k].to ]) 
357             visit[ edge[k].to ] = true;
358             pre[edge[k].to] = x;//记录父节点
359             DFS(edge[k].to);
360         
361     
362 
363 
364 int MVC() 
365     bool s[maxn] = 0;
366     bool set[maxn] = 0;
367     int ans = 0;
368     for(int i = n - 1; i >= 1; i--) //逆序进行贪心,排除掉其根节点
369         int t = newpos[i];
370         if(!s[t] && !s[ pre[t] ]) //如果当前节点和其父节点都不属于顶点覆盖集合
371             set[ pre[t] ] = true;//把其父节点加入到顶点覆盖集合
372             ans ++; //集合内顶点个数加 1
373             s[t] = true;//标记当前节点被覆盖
374             s[ pre[t] ] = true;//标记其父节点被覆盖
375                 
376     
377     return ans;
378 
379 
380 void solve() 
381     memset(visit, false, sizeof(visit));//初始化
382     now = 0;
383     visit[1] = true;
384     pre[1] = 1;
385     DFS(1);//从第一个根节点开始寻找遍历序列
386     MVC();
387 
388  *  * ****************************************************/
389 
390 /*******************************************************
391  * 最大独立集
392 
393 const int maxn = 1000 + 5;
394 int pre[maxn];//存储父节点
395 bool visit[maxn];//DFS标记数组
396 int newpos[maxn];//遍历序列
397 int now;
398 int n, m;
399 
400 int head[maxn];//链式前向星
401 struct Node int to; int next;;
402 Node edge[maxn];
403 
404 void DFS(int x) 
405     newpos[now ++] = x;//记录遍历序列
406     for(int k = head[x]; k != -1; k = edge[k].next) 
407         if(!visit[ edge[k].to ]) 
408             visit[ edge[k].to ] = true;
409             pre[edge[k].to] = x;//记录父节点
410             DFS(edge[k].to);
411         
412     
413 
414 
415 int MIS() 
416     bool s[maxn] = 0;
417     bool set[maxn] = 0;
418     int ans = 0;
419     for(int i = n - 1; i >= 0; i--) //按照DFS遍历序列的逆序进行贪心
420         int t = newpos[i];
421         if(!s[t]) //如果当前节点没有被覆盖
422             set[t] = true;//把当前节点加入到独立集
423             ans ++;//独立集中点的个数加 1
424             s[t] = true;//标记当前点已经被覆盖
425             s[ pre[t] ] = true;//标记当前点的父节点已经被覆盖
426                 
427     
428     return ans;
429 
430 
431 void solve() 
432     memset(visit, false, sizeof(visit));//初始化
433     now = 0;
434     visit[1] = true;
435     pre[1] = 1;
436     DFS(1);//从第一个根节点开始寻找遍历序列
437     MIS();
438 
439  *  * ***************************************************/
440 
441 
442 int main() 
443 
444     return 0;
445 

 

以上是关于二分匹配模板的主要内容,如果未能解决你的问题,请参考以下文章

二分图匹配([洛谷]P3386 模板 二分图匹配)

二分图匹配(模板)

P3386 模板二分图匹配

luoguP3386 模板二分图匹配

洛谷—— P3386 模板二分图匹配

861. 二分图的最大匹配(匈牙利算法模板)