This documentation is automatically generated by online-judge-tools/verification-helper
#include "GraphTheory/BipartiteMatching.hpp"
二部グラフ上の最大マッチングを求めます。
計算量は、$2$ つの頂点集合をそれぞれ $S$, $T$ 、辺の数を $M$ として $\mathcal{O}(\min(|S|, |T|) M)$ です。
実際はかなり高速です。
BipartiteMatching(int x, int y)
BipartiteMatching(int n)
void add_edge(int a, int b)
void build()
int max_matching()
std::vector<std::pair<int, int>> matching()
int matching_x(int k)
int matching_y(int k)
以下、$2$ つの頂点集合をそれぞれ $S$, $T$ 、辺の数を $M$ とします。
$|S| = x, |T| = y$ で初期化します。
制約
計算量
$|S| = |T| = n$ で初期化します。
制約
計算量
$S$ の頂点 $a$ と $T$ の頂点 $b$ の間に辺を張ります。
計算量
$S, T$ の最大マッチングを求めます。 必ず事前に呼んでください。
計算量
$S, T$ の最大マッチングでのマッチング数を返します。
計算量
$S, T$ の最大マッチングでのそれぞれのマッチングの組 ($S, T$ の順) を返します。
計算量
$S, T$ の最大マッチングでの $S$ の頂点 $k$ のマッチング先を返します。 存在しなければ $-1$ を返します。
制約
計算量
$S, T$ の最大マッチングでの $T$ の頂点 $k$ のマッチング先を返します。 存在しなければ $-1$ を返します。
制約
計算量
#include <bits/stdc++.h>
#include "GraphTheory/BipartiteMatching.hpp"
using namespace std;
int main() {
BipartiteMatching bm(3, 4);
// S -> T
// 0 -> 0 1
// 1 -> 1
// 2 -> 1 3
bm.add_edge(0, 0);
bm.add_edge(0, 1);
bm.add_edge(1, 1);
bm.add_edge(2, 1);
bm.add_edge(2, 3);
bm.build(); // 必ず呼ぶ
cout << "max_matching() = " << bm.max_matching() << endl; // 3
/*
matching : 0 - 0
matching : 1 - 1
matching : 2 - 3
*/
for (auto [x, y] : bm.matching()) cout << "matching : " << x << " - " << y << endl;
/*
S: x = 0 - 0
S: x = 1 - 1
S: x = 2 - 3
T: y = 0 - 0
T: y = 1 - 1
T: y = 2 - -1
T: y = 3 - 2
*/
for (int i = 0; i < 3; ++i) cout << "S: x = " << i << " - " << bm.matching_x(i) << endl;
for (int i = 0; i < 4; ++i) cout << "T: y = " << i << " - " << bm.matching_y(i) << endl;
}
2021/03/13: https://manabitimes.jp/math/1147
2020/03/05: https://ikatakos.com/pot/programming_algorithm/graph_theory/bipartite_matching
2020/08/26: https://snuke.hatenablog.com/entry/2019/05/07/013609
#ifndef INCLUDE_GUARD_BIPARTITE_MATCHING_HPP
#define INCLUDE_GUARD_BIPARTITE_MATCHING_HPP
#include <vector>
#include <cassert>
#include <utility>
#include <queue>
/**
* @brief https://tkmst201.github.io/Library/GraphTheory/BipartiteMatching.hpp
*/
struct BipartiteMatching {
private:
using Graph = std::vector<std::vector<int>>;
Graph g;
int x, y;
bool isswap;
int max_maching_;
std::vector<int> match_x, match_y;
bool isbuilt = false;
public:
BipartiteMatching(int x, int y)
: g(std::min(x, y)), x(std::min(x, y)), y(std::max(x, y)), isswap(x > y) {}
BipartiteMatching(int n) : BipartiteMatching(n, n) {}
void add_edge(int a, int b) {
if (isswap) std::swap(a, b);
assert(0 <= a && a < x);
assert(0 <= b && b < y);
g[a].emplace_back(b);
isbuilt = false;
}
void build() {
match_y.assign(y, -1);
match_x.assign(x, -1);
max_maching_ = 0;
int c = 1;
std::vector<int> visited(x, 0);
bool update = false;
auto dfs = [&](auto self, int u) -> bool {
visited[u] = c;
for (int v : g[u]) if (match_y[v] == -1) { match_y[v] = u; match_x[u] = v; return true; }
for (int v : g[u]) if (visited[match_y[v]] >= 0 && visited[match_y[v]] != c && self(self, match_y[v])) { match_y[v] = u; match_x[u] = v; return true; }
if (!update) visited[u] = -1;
return false;
};
std::queue<int> que;
for (int i = 0; i < x; ++i) {
if (dfs(dfs, i)) ++max_maching_, update = true;
else if (update) que.emplace(i);
}
while (!que.empty()) {
++c;
const int n = que.size();
update = false;
for (int i = 0; i < n; ++i) {
const int u = que.front();
que.pop();
if (match_x[u] == -1 && visited[u] != c && dfs(dfs, u)) {
++max_maching_;
update = true;
}
else if (update) que.emplace(u);
}
}
isbuilt = true;
}
int max_matching() const noexcept {
assert(isbuilt);
return max_maching_;
}
std::vector<std::pair<int, int>> matching() const {
assert(isbuilt);
std::vector<std::pair<int, int>> res;
for (int i = 0; i < x; ++i) {
if (match_x[i] == -1) continue;
if (isswap) res.emplace_back(match_x[i], i);
else res.emplace_back(i, match_x[i]);
}
return res;
}
int matching_x(int k) const noexcept {
assert(isbuilt);
assert(0 <= k && k < (isswap ? y : x));
return isswap ? match_y[k] : match_x[k];
}
int matching_y(int k) const noexcept {
assert(isbuilt);
assert(0 <= k && k < (isswap ? x : y));
return isswap ? match_x[k] : match_y[k];
}
};
#endif // INCLUDE_GUARD_BIPARTITE_MATCHING_HPP
#line 1 "GraphTheory/BipartiteMatching.hpp"
#include <vector>
#include <cassert>
#include <utility>
#include <queue>
/**
* @brief https://tkmst201.github.io/Library/GraphTheory/BipartiteMatching.hpp
*/
struct BipartiteMatching {
private:
using Graph = std::vector<std::vector<int>>;
Graph g;
int x, y;
bool isswap;
int max_maching_;
std::vector<int> match_x, match_y;
bool isbuilt = false;
public:
BipartiteMatching(int x, int y)
: g(std::min(x, y)), x(std::min(x, y)), y(std::max(x, y)), isswap(x > y) {}
BipartiteMatching(int n) : BipartiteMatching(n, n) {}
void add_edge(int a, int b) {
if (isswap) std::swap(a, b);
assert(0 <= a && a < x);
assert(0 <= b && b < y);
g[a].emplace_back(b);
isbuilt = false;
}
void build() {
match_y.assign(y, -1);
match_x.assign(x, -1);
max_maching_ = 0;
int c = 1;
std::vector<int> visited(x, 0);
bool update = false;
auto dfs = [&](auto self, int u) -> bool {
visited[u] = c;
for (int v : g[u]) if (match_y[v] == -1) { match_y[v] = u; match_x[u] = v; return true; }
for (int v : g[u]) if (visited[match_y[v]] >= 0 && visited[match_y[v]] != c && self(self, match_y[v])) { match_y[v] = u; match_x[u] = v; return true; }
if (!update) visited[u] = -1;
return false;
};
std::queue<int> que;
for (int i = 0; i < x; ++i) {
if (dfs(dfs, i)) ++max_maching_, update = true;
else if (update) que.emplace(i);
}
while (!que.empty()) {
++c;
const int n = que.size();
update = false;
for (int i = 0; i < n; ++i) {
const int u = que.front();
que.pop();
if (match_x[u] == -1 && visited[u] != c && dfs(dfs, u)) {
++max_maching_;
update = true;
}
else if (update) que.emplace(u);
}
}
isbuilt = true;
}
int max_matching() const noexcept {
assert(isbuilt);
return max_maching_;
}
std::vector<std::pair<int, int>> matching() const {
assert(isbuilt);
std::vector<std::pair<int, int>> res;
for (int i = 0; i < x; ++i) {
if (match_x[i] == -1) continue;
if (isswap) res.emplace_back(match_x[i], i);
else res.emplace_back(i, match_x[i]);
}
return res;
}
int matching_x(int k) const noexcept {
assert(isbuilt);
assert(0 <= k && k < (isswap ? y : x));
return isswap ? match_y[k] : match_x[k];
}
int matching_y(int k) const noexcept {
assert(isbuilt);
assert(0 <= k && k < (isswap ? x : y));
return isswap ? match_x[k] : match_y[k];
}
};