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