use std::collections::VecDeque;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Edge {
right: usize,
score: i32,
}
#[derive(Debug, Default, Clone)]
pub struct BipartiteMatching {
n_left: usize,
n_right: usize,
adj: Vec<Vec<Edge>>,
match_left: Vec<Option<usize>>,
match_right: Vec<Option<usize>>,
dist: Vec<Option<usize>>,
matched: usize,
}
impl BipartiteMatching {
#[must_use]
pub const fn new() -> Self {
Self {
n_left: 0,
n_right: 0,
adj: Vec::new(),
match_left: Vec::new(),
match_right: Vec::new(),
dist: Vec::new(),
matched: 0,
}
}
#[must_use]
pub fn with_shape(n_left: usize, n_right: usize) -> Self {
let mut matching = Self::new();
matching.reset(n_left, n_right);
matching
}
pub fn reset(&mut self, n_left: usize, n_right: usize) {
self.n_left = n_left;
self.n_right = n_right;
self.matched = 0;
self.adj.resize_with(n_left, Vec::new);
self.adj.truncate(n_left);
for edges in &mut self.adj {
edges.clear();
}
self.match_left.clear();
self.match_left.resize(n_left, None);
self.match_right.clear();
self.match_right.resize(n_right, None);
self.dist.clear();
self.dist.resize(n_left + 1, None);
}
pub fn add_edge(&mut self, left: usize, right: usize) {
self.add_scored_edge(left, right, 0);
}
pub fn add_scored_edge(&mut self, left: usize, right: usize, score: i32) {
if left >= self.n_left || right >= self.n_right {
return;
}
self.adj[left].push(Edge { right, score });
}
pub fn solve(&mut self) -> usize {
self.matched = 0;
for edges in &mut self.match_left {
*edges = None;
}
for edges in &mut self.match_right {
*edges = None;
}
for edges in &mut self.adj {
edges.sort_by(|a, b| b.score.cmp(&a.score));
}
while self.bfs() {
for u in 0..self.n_left {
if self.match_left[u].is_none() && self.dfs(u) {
self.matched += 1;
}
}
}
self.matched
}
fn bfs(&mut self) -> bool {
let sentinel = self.n_left;
let mut queue = VecDeque::with_capacity(self.n_left);
for u in 0..self.n_left {
if self.match_left[u].is_none() {
self.dist[u] = Some(0);
queue.push_back(u);
} else {
self.dist[u] = None; }
}
self.dist[sentinel] = None;
while let Some(u) = queue.pop_front() {
let closer = match (self.dist[u], self.dist[sentinel]) {
(Some(_), None) => true,
(Some(du), Some(ds)) => du < ds,
(None, _) => false,
};
if !closer {
continue;
}
let Some(du) = self.dist[u] else { continue };
for index in 0..self.adj[u].len() {
let v = self.adj[u][index].right;
let next = self.match_right[v].unwrap_or(sentinel);
if self.dist[next].is_none() {
self.dist[next] = Some(du + 1);
if next != sentinel {
queue.push_back(next);
}
}
}
}
self.dist[sentinel].is_some()
}
fn dfs(&mut self, u: usize) -> bool {
let sentinel = self.n_left;
if u == sentinel {
return true; }
let Some(du) = self.dist[u] else {
return false;
};
for index in 0..self.adj[u].len() {
let v = self.adj[u][index].right;
let next = self.match_right[v].unwrap_or(sentinel);
if self.dist[next] == Some(du + 1) && self.dfs(next) {
self.match_right[v] = Some(u);
self.match_left[u] = Some(v);
return true;
}
}
self.dist[u] = None; false
}
#[must_use]
pub fn match_for_left(&self, left: usize) -> Option<usize> {
self.match_left.get(left).copied().flatten()
}
#[must_use]
pub fn match_for_right(&self, right: usize) -> Option<usize> {
self.match_right.get(right).copied().flatten()
}
#[must_use]
pub const fn matched_count(&self) -> usize {
self.matched
}
#[must_use]
pub const fn left_len(&self) -> usize {
self.n_left
}
#[must_use]
pub const fn right_len(&self) -> usize {
self.n_right
}
}