use crate::error::OptimizeError;
pub type CoveringResult<T> = Result<T, OptimizeError>;
pub fn greedy_set_cover(universe: usize, sets: &[Vec<usize>]) -> Vec<usize> {
if universe == 0 || sets.is_empty() {
return vec![];
}
let mut uncovered = vec![true; universe];
let mut remaining = universe;
let mut selected = Vec::new();
let mut used = vec![false; sets.len()];
while remaining > 0 {
let mut best_idx = None;
let mut best_count = 0usize;
for (i, set) in sets.iter().enumerate() {
if used[i] {
continue;
}
let count = set.iter().filter(|&&e| e < universe && uncovered[e]).count();
if count > best_count {
best_count = count;
best_idx = Some(i);
}
}
match best_idx {
Some(idx) => {
used[idx] = true;
selected.push(idx);
for &e in &sets[idx] {
if e < universe && uncovered[e] {
uncovered[e] = false;
remaining -= 1;
}
}
}
None => break, }
}
selected
}
pub fn weighted_set_cover(
universe: usize,
sets: &[Vec<usize>],
costs: &[f64],
) -> CoveringResult<(Vec<usize>, f64)> {
if sets.len() != costs.len() {
return Err(OptimizeError::InvalidInput(format!(
"sets.len() = {} but costs.len() = {}",
sets.len(),
costs.len()
)));
}
if universe == 0 || sets.is_empty() {
return Ok((vec![], 0.0));
}
let mut uncovered = vec![true; universe];
let mut remaining = universe;
let mut selected = Vec::new();
let mut total_cost = 0.0;
let mut used = vec![false; sets.len()];
while remaining > 0 {
let mut best_idx = None;
let mut best_ratio = f64::INFINITY;
for (i, set) in sets.iter().enumerate() {
if used[i] {
continue;
}
let new_covers = set.iter().filter(|&&e| e < universe && uncovered[e]).count();
if new_covers == 0 {
continue;
}
let ratio = costs[i] / new_covers as f64;
if ratio < best_ratio {
best_ratio = ratio;
best_idx = Some(i);
}
}
match best_idx {
Some(idx) => {
used[idx] = true;
selected.push(idx);
total_cost += costs[idx];
for &e in &sets[idx] {
if e < universe && uncovered[e] {
uncovered[e] = false;
remaining -= 1;
}
}
}
None => break,
}
}
Ok((selected, total_cost))
}
pub fn vertex_cover_2approx(n: usize, edges: &[(usize, usize)]) -> Vec<usize> {
if n == 0 || edges.is_empty() {
return vec![];
}
let mut in_cover = vec![false; n];
let mut cover = Vec::new();
for &(u, v) in edges {
if u >= n || v >= n {
continue;
}
if !in_cover[u] && !in_cover[v] {
in_cover[u] = true;
in_cover[v] = true;
cover.push(u);
cover.push(v);
}
}
cover.sort_unstable();
cover.dedup();
cover
}
pub fn min_vertex_cover_bip(
n_left: usize,
n_right: usize,
edges: &[(usize, usize)],
) -> Vec<usize> {
if n_left == 0 || n_right == 0 {
return vec![];
}
let mut adj_left: Vec<Vec<usize>> = vec![vec![]; n_left];
for &(u, v) in edges {
if u < n_left && v < n_right {
adj_left[u].push(v);
}
}
let mut match_left = vec![usize::MAX; n_left];
let mut match_right = vec![usize::MAX; n_right];
for u in 0..n_left {
let mut visited = vec![false; n_right];
augment_bip(u, &adj_left, &mut match_left, &mut match_right, &mut visited);
}
let mut reachable_left = vec![false; n_left];
let mut reachable_right = vec![false; n_right];
let mut stack: Vec<(bool, usize)> = Vec::new(); for u in 0..n_left {
if match_left[u] == usize::MAX {
stack.push((true, u));
reachable_left[u] = true;
}
}
while let Some((is_left, v)) = stack.pop() {
if is_left {
for &r in &adj_left[v] {
if !reachable_right[r] {
reachable_right[r] = true;
stack.push((false, r));
}
}
} else {
let ml = match_right[v];
if ml != usize::MAX && !reachable_left[ml] {
reachable_left[ml] = true;
stack.push((true, ml));
}
}
}
let mut cover = Vec::new();
for u in 0..n_left {
if !reachable_left[u] {
cover.push(u); }
}
for v in 0..n_right {
if reachable_right[v] {
cover.push(n_left + v); }
}
cover.sort_unstable();
cover
}
fn augment_bip(
u: usize,
adj: &[Vec<usize>],
match_left: &mut Vec<usize>,
match_right: &mut Vec<usize>,
visited: &mut Vec<bool>,
) -> bool {
for &v in &adj[u] {
if visited[v] {
continue;
}
visited[v] = true;
let prev = match_right[v];
if prev == usize::MAX
|| augment_bip(prev, adj, match_left, match_right, visited)
{
match_left[u] = v;
match_right[v] = u;
return true;
}
}
false
}
pub fn hitting_set(universe: usize, sets: &[Vec<usize>]) -> Vec<usize> {
if universe == 0 || sets.is_empty() {
return vec![];
}
let m = sets.len();
let mut hit = vec![false; m]; let mut remaining = m;
let mut chosen = Vec::new();
let mut in_hitting_set = vec![false; universe];
while remaining > 0 {
let mut best_elem = None;
let mut best_count = 0usize;
for e in 0..universe {
if in_hitting_set[e] {
continue;
}
let count = sets
.iter()
.enumerate()
.filter(|(i, s)| !hit[*i] && s.contains(&e))
.count();
if count > best_count {
best_count = count;
best_elem = Some(e);
}
}
match best_elem {
Some(e) => {
in_hitting_set[e] = true;
chosen.push(e);
for (i, s) in sets.iter().enumerate() {
if !hit[i] && s.contains(&e) {
hit[i] = true;
remaining -= 1;
}
}
}
None => break, }
}
chosen.sort_unstable();
chosen
}
#[cfg(test)]
mod tests {
use super::*;
fn is_set_cover(universe: usize, sets: &[Vec<usize>], selected: &[usize]) -> bool {
let mut covered = vec![false; universe];
for &idx in selected {
for &e in &sets[idx] {
if e < universe {
covered[e] = true;
}
}
}
covered.iter().all(|&c| c)
}
#[test]
fn test_greedy_set_cover() {
let sets = vec![
vec![0, 1, 2],
vec![1, 3],
vec![2, 4],
vec![3, 4],
];
let sel = greedy_set_cover(5, &sets);
assert!(is_set_cover(5, &sets, &sel));
assert!(!sel.is_empty());
}
#[test]
fn test_weighted_set_cover() {
let sets = vec![vec![0, 1, 2], vec![1, 3], vec![2, 4], vec![3, 4]];
let costs = vec![3.0, 1.0, 1.0, 1.0];
let (sel, cost) = weighted_set_cover(5, &sets, &costs).expect("unexpected None or Err");
assert!(is_set_cover(5, &sets, &sel));
assert!(cost > 0.0);
}
#[test]
fn test_weighted_set_cover_mismatch() {
let sets = vec![vec![0, 1]];
let costs = vec![1.0, 2.0]; assert!(weighted_set_cover(2, &sets, &costs).is_err());
}
#[test]
fn test_vertex_cover_2approx() {
let edges = vec![(0, 1), (1, 2), (2, 0)];
let cover = vertex_cover_2approx(3, &edges);
for &(u, v) in &edges {
assert!(cover.contains(&u) || cover.contains(&v));
}
}
#[test]
fn test_vertex_cover_path() {
let edges = vec![(0, 1), (1, 2), (2, 3)];
let cover = vertex_cover_2approx(4, &edges);
for &(u, v) in &edges {
assert!(cover.contains(&u) || cover.contains(&v));
}
assert!(cover.len() <= 4); }
#[test]
fn test_kings_bipartite() {
let edges = vec![(0, 0), (0, 1), (1, 0), (1, 1)];
let cover = min_vertex_cover_bip(2, 2, &edges);
for &(u, v) in &edges {
assert!(cover.contains(&u) || cover.contains(&(2 + v)));
}
assert_eq!(cover.len(), 2);
}
#[test]
fn test_kings_path_bip() {
let edges = vec![(0, 0)];
let cover = min_vertex_cover_bip(1, 1, &edges);
assert_eq!(cover.len(), 1);
assert!(cover.contains(&0) || cover.contains(&1));
}
#[test]
fn test_hitting_set() {
let sets = vec![vec![0, 1], vec![1, 2], vec![2, 3], vec![3, 4]];
let hs = hitting_set(5, &sets);
for s in &sets {
assert!(s.iter().any(|e| hs.contains(e)));
}
}
#[test]
fn test_empty_inputs() {
assert!(greedy_set_cover(0, &[]).is_empty());
assert!(vertex_cover_2approx(0, &[]).is_empty());
assert!(hitting_set(0, &[]).is_empty());
}
}