diff --git a/library/graphs/hopcroft_karp.hpp b/library/graphs/hopcroft_karp.hpp deleted file mode 100644 index 9071264b..00000000 --- a/library/graphs/hopcroft_karp.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once -//! https://github.com/foreverbell/acm-icpc-cheat-sheet/blob/master/src/graph-algorithm/hopcroft-karp.cpp -//! @code -//! vector> g(lsz); -//! g[l] += r; // add edge l <-> r -//! // 0<=l to_r[l] in matching if to_r[l]!=-1 -//! to_l[r] <-> r in matching if to_l[r]!=-1 -//! mvc_l[l] is 1 if l in Min Vertex Cover -//! mvc_r[r] is 1 if r in Min Vertex Cover -//! @time O(n + m * sqrt(n)) n = lsz + rsz -//! @space O(n) -struct hopcroft_karp { - int m_sz = 0; - vi to_r, to_l; - vector mvc_l, mvc_r; - hopcroft_karp(const auto& g, int rsz): - to_r(sz(g), -1), to_l(rsz, -1) { - int lsz = sz(g); - while (1) { - queue q; - vi level(lsz, -1); - rep(i, 0, lsz) if (to_r[i] == -1) level[i] = 0, - q.push(i); - bool found = 0; - mvc_l.assign(lsz, 1); - mvc_r.assign(rsz, 0); - while (!empty(q)) { - int u = q.front(); - q.pop(); - mvc_l[u] = 0; - for (int v : g[u]) { - mvc_r[v] = 1; - int w = to_l[v]; - if (w == -1) found = 1; - else if (level[w] == -1) { - level[w] = level[u] + 1; - q.push(w); - } - } - } - if (!found) break; - auto dfs = [&](auto dfs, int u) -> bool { - for (int v : g[u]) { - int w = to_l[v]; - if (w == -1 || - (level[u] + 1 == level[w] && dfs(dfs, w))) { - to_r[u] = v; - to_l[v] = u; - return 1; - } - } - level[u] = INT_MAX; - return 0; - }; - rep(i, 0, lsz) - m_sz += (to_r[i] == -1 && dfs(dfs, i)); - } - } -}; diff --git a/library/graphs/min_vertex_cover.hpp b/library/graphs/min_vertex_cover.hpp new file mode 100644 index 00000000..0037e6a8 --- /dev/null +++ b/library/graphs/min_vertex_cover.hpp @@ -0,0 +1,13 @@ +#include "../../kactl/content/graph/HopcroftKarp.h" +pair cover(const vector& g, vi& r) { + int n = sz(g), t = 0; + vi cl(n), cr(sz(r)), q(n); + for (int u : r) + if (u != -1) cl[u] = 1; + rep(i, 0, n) if (!cl[i]) q[t++] = i; + rep(i, 0, t) for (int v : g[q[i]]) { + cr[v] = 1; + if (r[v] != -1 && cl[r[v]]) cl[q[t++] = r[v]] = 0; + } + return {cl, cr}; +} diff --git a/tests/library_checker_aizu_tests/graphs/hopcroft_karp_aizu.test.cpp b/tests/library_checker_aizu_tests/graphs/hopcroft_karp_aizu.test.cpp index 6bc117e4..70840298 100644 --- a/tests/library_checker_aizu_tests/graphs/hopcroft_karp_aizu.test.cpp +++ b/tests/library_checker_aizu_tests/graphs/hopcroft_karp_aizu.test.cpp @@ -1,7 +1,7 @@ #define PROBLEM \ "https://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_7_A" #include "../template.hpp" -#include "../../../library/graphs/hopcroft_karp.hpp" +#include "../../../library/graphs/min_vertex_cover.hpp" int main() { cin.tie(0)->sync_with_stdio(0); int l, r, m; @@ -14,34 +14,18 @@ int main() { adj[u].push_back(v); edges.emplace_back(u, v); } - hopcroft_karp res(adj, r); - cout << res.m_sz << '\n'; - // asserting correctness of both to_r, and to_l - int size_l = 0; - for (int i = 0; i < l; i++) { - if (res.to_r[i] != -1) { - size_l++; - int node_r = res.to_r[i]; - assert(res.to_l[node_r] == i); - } - } + vi ri(r, -1); + int size_matching = hopcroftKarp(adj, ri); + auto [mvc_l, mvc_r] = cover(adj, ri); int size_r = 0; - for (int i = 0; i < r; i++) { - if (res.to_l[i] != -1) { - size_r++; - int node_l = res.to_l[i]; - assert(res.to_r[node_l] == i); - } - } - assert(size_l == res.m_sz); - assert(size_r == res.m_sz); + for (int i = 0; i < r; i++) + if (ri[i] != -1) size_r++; + assert(size_r == size_matching); // asserting found min vertex cover is correct - int cnt = - accumulate(begin(res.mvc_l), end(res.mvc_l), 0) + - accumulate(begin(res.mvc_r), end(res.mvc_r), 0); - assert(cnt == res.m_sz); // size of min vertex cover - // == size of max matching - for (auto [u, v] : edges) - assert(res.mvc_l[u] || res.mvc_r[v]); + int cnt = accumulate(begin(mvc_l), end(mvc_l), 0) + + accumulate(begin(mvc_r), end(mvc_r), 0); + assert(cnt == size_matching); + for (auto [u, v] : edges) assert(mvc_l[u] || mvc_r[v]); + cout << size_matching << '\n'; return 0; } diff --git a/tests/library_checker_aizu_tests/graphs/hopcroft_karp_lib_checker.test.cpp b/tests/library_checker_aizu_tests/graphs/hopcroft_karp_lib_checker.test.cpp index a81f0fac..268030f0 100644 --- a/tests/library_checker_aizu_tests/graphs/hopcroft_karp_lib_checker.test.cpp +++ b/tests/library_checker_aizu_tests/graphs/hopcroft_karp_lib_checker.test.cpp @@ -1,7 +1,7 @@ #define PROBLEM \ "https://judge.yosupo.jp/problem/bipartitematching" #include "../template.hpp" -#include "../../../library/graphs/hopcroft_karp.hpp" +#include "../../../library/graphs/min_vertex_cover.hpp" int main() { cin.tie(0)->sync_with_stdio(0); int l, r, m; @@ -14,36 +14,21 @@ int main() { adj[u].push_back(v); edges.emplace_back(u, v); } - hopcroft_karp res(adj, r); - cout << res.m_sz << '\n'; - // asserting correctness of both to_r, and to_l (as - // well as printing answer) - int size_l = 0; - for (int i = 0; i < l; i++) { - if (res.to_r[i] != -1) { - size_l++; - int node_r = res.to_r[i]; - cout << i << " " << node_r << '\n'; - assert(res.to_l[node_r] == i); - } - } + vi ri(r, -1); + int m_sz = hopcroftKarp(adj, ri); + auto [mvc_l, mvc_r] = cover(adj, ri); + cout << m_sz << '\n'; int size_r = 0; for (int i = 0; i < r; i++) { - if (res.to_l[i] != -1) { + if (ri[i] != -1) { size_r++; - int node_l = res.to_l[i]; - assert(res.to_r[node_l] == i); + cout << ri[i] << ' ' << i << '\n'; } } - assert(size_l == res.m_sz); - assert(size_r == res.m_sz); - // asserting found min vertex cover is correct - int cnt = - accumulate(begin(res.mvc_l), end(res.mvc_l), 0) + - accumulate(begin(res.mvc_r), end(res.mvc_r), 0); - assert(cnt == res.m_sz); // size of min vertex cover - // == size of max matching - for (auto [u, v] : edges) - assert(res.mvc_l[u] || res.mvc_r[v]); + assert(size_r == m_sz); + int cnt = accumulate(begin(mvc_l), end(mvc_l), 0) + + accumulate(begin(mvc_r), end(mvc_r), 0); + assert(cnt == m_sz); + for (auto [u, v] : edges) assert(mvc_l[u] || mvc_r[v]); return 0; }