二分匹配模板

和自甴很熟 提交于 2020-03-05 06:02:16
  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 }

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!