#[derive(Clone, Debug)]
pub struct Graph {
n: usize,
adj: Vec<Vec<usize>>,
}
impl Graph {
pub fn new(n: usize) -> Self {
Graph {
n,
adj: vec![Vec::new(); n],
}
}
pub fn len(&self) -> usize {
self.n
}
pub fn is_empty(&self) -> bool {
self.n == 0
}
pub fn add_edge(&mut self, u: usize, v: usize) {
assert!(u < self.n && v < self.n, "Invalid vertex index");
self.adj[u].push(v);
self.adj[v].push(u);
}
}
pub fn edmonds_blossom_max_matching(g: &Graph) -> Vec<Option<usize>> {
let n = g.len();
let inf = n; let mut matchv = vec![inf; n];
let mut p = vec![inf; n];
let mut base: Vec<usize> = (0..n).collect();
let _used = vec![false; n];
let _blossom = vec![false; n];
struct BlossomState<'a> {
p: &'a mut [usize],
base: &'a mut [usize],
matchv: &'a [usize],
blossom: &'a mut [bool],
}
fn lca(v: usize, w: usize, p: &[usize], base: &[usize], matchv: &[usize], inf: usize) -> usize {
let n = p.len();
let mut used_flag = vec![false; n];
let mut v = v;
loop {
v = base[v];
used_flag[v] = true;
if matchv[v] == inf {
break;
}
v = p[matchv[v]];
}
let mut w = w;
while !used_flag[base[w]] {
w = p[matchv[w]];
}
base[w]
}
fn mark_path(v: usize, b: usize, mut x: usize, state: &mut BlossomState) {
let mut v = v;
while state.base[v] != b {
state.blossom[state.base[v]] = true;
state.blossom[state.base[state.matchv[v]]] = true;
state.p[v] = x;
x = state.matchv[v];
v = state.p[state.matchv[v]];
}
}
fn find_path(
start: usize,
g: &Graph,
matchv: &[usize],
p: &mut [usize],
base: &mut [usize],
inf: usize,
) -> Option<usize> {
let n = g.len();
let mut used = vec![false; n];
let mut q = std::collections::VecDeque::new();
q.push_back(start);
used[start] = true;
p.iter_mut().for_each(|x| *x = inf);
while let Some(v) = q.pop_front() {
for &to in &g.adj[v] {
if base[v] == base[to] || matchv[v] == to {
continue;
}
if to == start || (matchv[to] != inf && p[matchv[to]] != inf) {
let cur = lca(v, to, p, base, matchv, inf);
let mut blossom_flag = vec![false; n];
let mut state = BlossomState {
p,
base,
matchv,
blossom: &mut blossom_flag,
};
mark_path(v, cur, to, &mut state);
mark_path(to, cur, v, &mut state);
for (i, &flag) in blossom_flag.iter().enumerate() {
if flag && base[i] == cur && !used[i] {
used[i] = true;
q.push_back(i);
}
}
} else if p[to] == inf {
p[to] = v;
if matchv[to] == inf {
return Some(to);
} else {
used[matchv[to]] = true;
q.push_back(matchv[to]);
}
}
}
}
None
}
fn augment_path(start: usize, finish: usize, matchv: &mut [usize], p: &[usize], inf: usize) {
let mut cur = finish;
while cur != start {
let prev = p[cur];
let nxt = matchv[prev];
matchv[cur] = prev;
matchv[prev] = cur;
cur = nxt;
if cur == inf {
break;
}
}
}
for v in 0..n {
if matchv[v] == inf {
if let Some(finish) = find_path(v, g, &matchv, &mut p, &mut base, inf) {
augment_path(v, finish, &mut matchv, &p, inf);
}
}
}
matchv
.into_iter()
.map(|x| if x == inf { None } else { Some(x) })
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_triangle() {
let mut g = Graph::new(3);
g.add_edge(0, 1);
g.add_edge(1, 2);
g.add_edge(2, 0);
let matching = edmonds_blossom_max_matching(&g);
let matched_edges: Vec<_> = matching
.iter()
.enumerate()
.filter_map(|(v, &m)| {
if let Some(u) = m {
if v < u {
Some((v, u))
} else {
None
}
} else {
None
}
})
.collect();
assert_eq!(
matched_edges.len(),
1,
"Triangle should yield 1 matched edge"
);
}
#[test]
fn test_square() {
let mut g = Graph::new(4);
g.add_edge(0, 1);
g.add_edge(1, 2);
g.add_edge(2, 3);
g.add_edge(3, 0);
g.add_edge(0, 2);
g.add_edge(1, 3);
let matching = edmonds_blossom_max_matching(&g);
let matched_edges: Vec<_> = matching
.iter()
.enumerate()
.filter_map(|(v, &m)| {
if let Some(u) = m {
if v < u {
Some((v, u))
} else {
None
}
} else {
None
}
})
.collect();
assert_eq!(
matched_edges.len(),
2,
"Square with diagonals should yield 2 matched edges"
);
}
#[test]
fn test_blossom_case() {
let mut g = Graph::new(5);
g.add_edge(0, 1);
g.add_edge(1, 2);
g.add_edge(2, 3);
g.add_edge(3, 4);
g.add_edge(4, 0);
g.add_edge(1, 3);
let matching = edmonds_blossom_max_matching(&g);
let matched_edges: Vec<_> = matching
.iter()
.enumerate()
.filter_map(|(v, &m)| {
if let Some(u) = m {
if v < u {
Some((v, u))
} else {
None
}
} else {
None
}
})
.collect();
assert_eq!(
matched_edges.len(),
2,
"Blossom case should yield 2 matched edges"
);
}
}