Skip to content

Commit 2ec70c2

Browse files
authored
Add Bipartite Matching Kuhn's Algorithm (rust-lang#377)
1 parent cadeec0 commit 2ec70c2

File tree

2 files changed

+126
-1
lines changed

2 files changed

+126
-1
lines changed

src/graph/bipartite_matching.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Adjacency List
2+
type Graph = Vec<Vec<usize>>;
3+
4+
pub struct BipartiteMatching {
5+
pub adj: Graph,
6+
pub num_vertices_grp1: usize,
7+
pub num_vertices_grp2: usize,
8+
// mt[i] = v is the matching of i in grp1 to v in grp2
9+
pub mt: Vec<i32>,
10+
pub used: Vec<bool>,
11+
}
12+
impl BipartiteMatching {
13+
pub fn new(num_vertices_grp1: usize, num_vertices_grp2: usize) -> Self {
14+
BipartiteMatching {
15+
adj: vec![vec![]; num_vertices_grp1 + 1],
16+
num_vertices_grp1,
17+
num_vertices_grp2,
18+
mt: vec![-1; num_vertices_grp2 + 1],
19+
used: vec![false; num_vertices_grp1 + 1],
20+
}
21+
}
22+
#[inline]
23+
// Add an undirected edge u-v in the graph
24+
pub fn add_edge(&mut self, u: usize, v: usize) {
25+
self.adj[u].push(v);
26+
// self.adj[v].push(u);
27+
}
28+
29+
fn try_kuhn(&mut self, cur: usize) -> bool {
30+
if self.used[cur] {
31+
return false;
32+
}
33+
self.used[cur] = true;
34+
for i in 0..self.adj[cur].len() {
35+
let to = self.adj[cur][i];
36+
if self.mt[to] == -1 || self.try_kuhn(self.mt[to] as usize) {
37+
self.mt[to] = cur as i32;
38+
return true;
39+
}
40+
}
41+
false
42+
}
43+
pub fn kuhn(&mut self) {
44+
self.mt = vec![-1; self.num_vertices_grp2 + 1];
45+
for v in 1..self.num_vertices_grp1 + 1 {
46+
self.used = vec![false; self.num_vertices_grp1 + 1];
47+
self.try_kuhn(v);
48+
}
49+
}
50+
pub fn print_matching(&self) {
51+
for i in 1..self.num_vertices_grp2 + 1 {
52+
if self.mt[i] == -1 {
53+
continue;
54+
}
55+
println!("Vertex {} in grp1 matched with {} grp2", self.mt[i], i)
56+
}
57+
}
58+
}
59+
#[cfg(test)]
60+
mod tests {
61+
use super::*;
62+
#[test]
63+
fn small_graph() {
64+
let n1 = 6;
65+
let n2 = 6;
66+
let mut g = BipartiteMatching::new(n1, n2);
67+
// vertex 1 in grp1 to vertex 1 in grp 2
68+
// denote the ith grp2 vertex as n1+i
69+
g.add_edge(1, 2);
70+
g.add_edge(1, 3);
71+
// 2 is not connected to any vertex
72+
g.add_edge(3, 4);
73+
g.add_edge(3, 1);
74+
g.add_edge(4, 3);
75+
g.add_edge(5, 3);
76+
g.add_edge(5, 4);
77+
g.add_edge(6, 6);
78+
g.kuhn();
79+
g.print_matching();
80+
let answer: Vec<i32> = vec![-1, 2, -1, 1, 3, 4, 6];
81+
for i in 1..g.mt.len() {
82+
if g.mt[i] == -1 {
83+
// 5 in group2 has no pair
84+
assert_eq!(i, 5);
85+
continue;
86+
}
87+
// 2 in group1 has no pair
88+
assert!(g.mt[i] != 2);
89+
assert_eq!(i as i32, answer[g.mt[i] as usize]);
90+
}
91+
}
92+
#[test]
93+
fn super_small_graph() {
94+
let n1 = 1;
95+
let n2 = 1;
96+
let mut g = BipartiteMatching::new(n1, n2);
97+
g.add_edge(1, 1);
98+
g.kuhn();
99+
g.print_matching();
100+
assert_eq!(g.mt[1], 1);
101+
}
102+
#[test]
103+
fn only_one_vertex_graph() {
104+
let n1 = 10;
105+
let n2 = 10;
106+
let mut g = BipartiteMatching::new(n1, n2);
107+
g.add_edge(1, 1);
108+
g.add_edge(2, 1);
109+
g.add_edge(3, 1);
110+
g.add_edge(4, 1);
111+
g.add_edge(5, 1);
112+
g.add_edge(6, 1);
113+
g.add_edge(7, 1);
114+
g.add_edge(8, 1);
115+
g.add_edge(9, 1);
116+
g.add_edge(10, 1);
117+
g.kuhn();
118+
g.print_matching();
119+
assert_eq!(g.mt[1], 1);
120+
for i in 2..g.mt.len() {
121+
assert!(g.mt[i] == -1);
122+
}
123+
}
124+
}

src/graph/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod bellman_ford;
2+
mod bipartite_matching;
23
mod breadth_first_search;
34
mod centroid_decomposition;
45
mod depth_first_search;
@@ -16,8 +17,8 @@ mod prufer_code;
1617
mod strongly_connected_components;
1718
mod topological_sort;
1819
mod two_satisfiability;
19-
2020
pub use self::bellman_ford::bellman_ford;
21+
pub use self::bipartite_matching::BipartiteMatching;
2122
pub use self::breadth_first_search::breadth_first_search;
2223
pub use self::centroid_decomposition::CentroidDecomposition;
2324
pub use self::depth_first_search::depth_first_search;

0 commit comments

Comments
 (0)