diff --git a/content/graphs/HopcroftKarp.h b/content/graphs/HopcroftKarp.h new file mode 100644 index 000000000..809cdc524 --- /dev/null +++ b/content/graphs/HopcroftKarp.h @@ -0,0 +1,34 @@ +/** + * Author: Adam Soltan + * Date: 2026-01-13 + * License: CC0 + * Description: Fast bipartite matching algorithm. Graph $g$ should be a list + * of neighbors of the left partition, and $r$ should be a vector full of + * $-1$'s of the same size as the right partition. Returns the size of + * the matching. $r[i]$ will be the match for vertex $i$ on the right side, + * or $-1$ if it's not matched. + * Time: O(E \sqrt{V}) + * Status: stress-tested by MinimumVertexCover and tested on Library Checker + */ +#pragma once + +int hopcroftKarp(vector& g, vi& r) { + int n = sz(g), res = 0; + vi l(n, -1), q(n), d(n); + auto dfs = [&](auto f, int u) -> bool { + int t = exchange(d[u], 0) + 1; + for (int v : g[u]) + if (r[v] == -1 || (d[r[v]] == t && f(f, r[v]))) + return l[u] = v, r[v] = u, 1; + return 0; + }; + for (int t = 0, f = 0;; t = f = 0, d.assign(n, 0)) { + rep(i,0,n) if (l[i] == -1) q[t++] = i, d[i] = 1; + rep(i,0,t) for (int v : g[q[i]]) { + if (r[v] == -1) f = 1; + else if (!d[r[v]]) d[r[v]] = d[q[i]] + 1, q[t++] = r[v]; + } + if (!f) return res; + rep(i,0,n) if (l[i] == -1) res += dfs(dfs, i); + } +} diff --git a/content/graphs/chapter.tex b/content/graphs/chapter.tex index db4a53742..15ea5cfb0 100644 --- a/content/graphs/chapter.tex +++ b/content/graphs/chapter.tex @@ -17,7 +17,7 @@ \section{Network flow} \kactlimport{MatroidIntersection.h} \section{Matching} - \kactlimport{hopcroftKarp.h} + \kactlimport{HopcroftKarp.h} \kactlimport{DFSMatching.h} \kactlimport{MinimumVertexCover.h} \kactlimport{WeightedMatching.h} diff --git a/content/graphs/hopcroftKarp.h b/content/graphs/hopcroftKarp.h deleted file mode 100644 index 83c9847a9..000000000 --- a/content/graphs/hopcroftKarp.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Author: Chen Xing - * Date: 2009-10-13 - * License: CC0 - * Source: N/A - * Description: Fast bipartite matching algorithm. Graph $g$ should be a list - * of neighbors of the left partition, and $btoa$ should be a vector full of - * -1's of the same size as the right partition. Returns the size of - * the matching. $btoa[i]$ will be the match for vertex $i$ on the right side, - * or $-1$ if it's not matched. - * Usage: vi btoa(m, -1); hopcroftKarp(g, btoa); - * Time: O(\sqrt{V}E) - * Status: stress-tested by MinimumVertexCover, and tested on oldkattis.adkbipmatch and SPOJ:MATCHING - */ -#pragma once - -bool dfs(int a, int L, vector& g, vi& btoa, vi& A, vi& B) { - if (A[a] != L) return 0; - A[a] = -1; - for (int b : g[a]) if (B[b] == L + 1) { - B[b] = 0; - if (btoa[b] == -1 || dfs(btoa[b], L + 1, g, btoa, A, B)) - return btoa[b] = a, 1; - } - return 0; -} - -int hopcroftKarp(vector& g, vi& btoa) { - int res = 0; - vi A(g.size()), B(btoa.size()), cur, next; - for (;;) { - fill(all(A), 0); - fill(all(B), 0); - /// Find the starting nodes for BFS (i.e. layer 0). - cur.clear(); - for (int a : btoa) if(a != -1) A[a] = -1; - rep(a,0,sz(g)) if(A[a] == 0) cur.push_back(a); - /// Find all layers using bfs. - for (int lay = 1;; lay++) { - bool islast = 0; - next.clear(); - for (int a : cur) for (int b : g[a]) { - if (btoa[b] == -1) { - B[b] = lay; - islast = 1; - } - else if (btoa[b] != a && !B[b]) { - B[b] = lay; - next.push_back(btoa[b]); - } - } - if (islast) break; - if (next.empty()) return res; - for (int a : next) A[a] = lay; - cur.swap(next); - } - /// Use DFS to scan for augmenting paths. - rep(a,0,sz(g)) - res += dfs(a, 0, g, btoa, A, B); - } -}