use std::collections::{HashSet, VecDeque};
pub fn minimum_dominating_set(edges: &[(usize, usize)], n_nodes: usize) -> Vec<usize> {
if n_nodes == 0 {
return vec![];
}
let adj = build_adj(edges, n_nodes);
let mut dominated = vec![false; n_nodes];
let mut in_set = vec![false; n_nodes];
let mut result = Vec::new();
fn vertex_coverage(v: usize, adj: &[Vec<usize>], dominated: &[bool]) -> usize {
let mut cnt = if !dominated[v] { 1 } else { 0 };
for &w in &adj[v] {
if !dominated[w] {
cnt += 1;
}
}
cnt
}
loop {
if dominated.iter().all(|&d| d) {
break;
}
let best = (0..n_nodes)
.filter(|&v| !in_set[v])
.max_by_key(|&v| vertex_coverage(v, &adj, &dominated));
match best {
None => break,
Some(v) => {
if vertex_coverage(v, &adj, &dominated) == 0 {
break;
}
in_set[v] = true;
result.push(v);
dominated[v] = true;
for &w in &adj[v] {
dominated[w] = true;
}
}
}
}
result.sort_unstable();
result
}
pub fn minimum_vertex_cover(edges: &[(usize, usize)], n_nodes: usize) -> Vec<usize> {
if n_nodes == 0 || edges.is_empty() {
return vec![];
}
let mut covered = HashSet::new();
let mut result = HashSet::new();
for &(u, v) in edges {
if u >= n_nodes || v >= n_nodes || u == v {
continue;
}
if !covered.contains(&u) && !covered.contains(&v) {
result.insert(u);
result.insert(v);
covered.insert(u);
covered.insert(v);
}
}
let mut vec: Vec<usize> = result.into_iter().collect();
vec.sort_unstable();
vec
}
pub fn maximum_independent_set(edges: &[(usize, usize)], n_nodes: usize) -> Vec<usize> {
if n_nodes == 0 {
return vec![];
}
let adj = build_adj(edges, n_nodes);
let mut active = vec![true; n_nodes];
let mut result = Vec::new();
loop {
let best = (0..n_nodes)
.filter(|&v| active[v])
.min_by_key(|&v| adj[v].iter().filter(|&&w| active[w]).count());
match best {
None => break,
Some(v) => {
result.push(v);
active[v] = false;
for &w in &adj[v] {
active[w] = false;
}
}
}
}
result.sort_unstable();
result
}
pub fn minimum_edge_dominating_set(
edges: &[(usize, usize)],
n_nodes: usize,
) -> Vec<(usize, usize)> {
if n_nodes == 0 || edges.is_empty() {
return vec![];
}
let mut matched = vec![false; n_nodes];
let mut result = Vec::new();
for &(u, v) in edges {
if u >= n_nodes || v >= n_nodes || u == v {
continue;
}
if !matched[u] && !matched[v] {
result.push(if u < v { (u, v) } else { (v, u) });
matched[u] = true;
matched[v] = true;
}
}
result
}
pub fn feedback_vertex_set(edges: &[(usize, usize)], n_nodes: usize) -> Vec<usize> {
if n_nodes == 0 {
return vec![];
}
let mut adj = build_adj(edges, n_nodes);
let mut removed = vec![false; n_nodes];
let mut result = Vec::new();
loop {
match find_cycle(&adj, n_nodes, &removed) {
None => break,
Some(cycle) => {
let best = cycle
.iter()
.max_by_key(|&&v| adj[v].iter().filter(|&&w| !removed[w]).count())
.copied();
if let Some(v) = best {
removed[v] = true;
result.push(v);
for w in 0..n_nodes {
adj[w].retain(|&x| x != v);
}
adj[v].clear();
}
}
}
}
result.sort_unstable();
result
}
fn find_cycle(adj: &[Vec<usize>], n: usize, removed: &[bool]) -> Option<Vec<usize>> {
let mut colour = vec![0u8; n]; let mut parent = vec![usize::MAX; n];
for start in 0..n {
if colour[start] != 0 || removed[start] {
continue;
}
let mut stack: Vec<(usize, usize)> = vec![(start, usize::MAX)]; while let Some((v, ni)) = stack.last_mut().copied() {
if colour[v] == 0 {
colour[v] = 1;
}
let neighbours: Vec<usize> = adj[v]
.iter()
.copied()
.filter(|&w| !removed[w])
.collect();
let mut found_next = false;
for idx in ni..neighbours.len() {
let w = neighbours[idx];
if let Some((_, ni_ref)) = stack.last_mut() {
*ni_ref = idx + 1;
}
if colour[w] == 1 && parent[v] != w {
let mut cycle = vec![w, v];
let mut cur = v;
while cur != w {
cur = parent[cur];
if cur == usize::MAX {
break;
}
cycle.push(cur);
}
return Some(cycle);
}
if colour[w] == 0 {
parent[w] = v;
stack.push((w, 0));
found_next = true;
break;
}
}
if !found_next {
colour[v] = 2;
stack.pop();
}
}
}
None
}
fn build_adj(edges: &[(usize, usize)], n: usize) -> Vec<Vec<usize>> {
let mut adj = vec![vec![]; n];
for &(u, v) in edges {
if u < n && v < n && u != v {
adj[u].push(v);
adj[v].push(u);
}
}
adj
}
#[allow(dead_code)]
fn is_connected_after_removal(adj: &[Vec<usize>], removed: &[bool], n: usize) -> bool {
let start = (0..n).find(|&v| !removed[v]);
let start = match start {
Some(s) => s,
None => return true,
};
let mut visited = vec![false; n];
let mut queue = VecDeque::new();
queue.push_back(start);
visited[start] = true;
let mut count = 1usize;
while let Some(v) = queue.pop_front() {
for &w in &adj[v] {
if !visited[w] && !removed[w] {
visited[w] = true;
count += 1;
queue.push_back(w);
}
}
}
count == n - removed.iter().filter(|&&r| r).count()
}
#[cfg(test)]
mod tests {
use super::*;
fn triangle() -> Vec<(usize, usize)> {
vec![(0, 1), (1, 2), (0, 2)]
}
fn path4() -> Vec<(usize, usize)> {
vec![(0, 1), (1, 2), (2, 3)]
}
fn k4() -> Vec<(usize, usize)> {
vec![(0,1),(0,2),(0,3),(1,2),(1,3),(2,3)]
}
#[test]
fn test_dominating_set_triangle() {
let ds = minimum_dominating_set(&triangle(), 3);
assert!(!ds.is_empty(), "dominating set must be non-empty");
let ds_set: HashSet<usize> = ds.iter().copied().collect();
for v in 0..3 {
let dominated = ds_set.contains(&v)
|| triangle().iter().any(|&(u, w)| {
(u == v && ds_set.contains(&w)) || (w == v && ds_set.contains(&u))
});
assert!(dominated, "vertex {v} is not dominated");
}
}
#[test]
fn test_dominating_set_path4() {
let ds = minimum_dominating_set(&path4(), 4);
let ds_set: HashSet<usize> = ds.iter().copied().collect();
for v in 0..4 {
let dominated = ds_set.contains(&v)
|| path4()
.iter()
.any(|&(u, w)| (u == v && ds_set.contains(&w)) || (w == v && ds_set.contains(&u)));
assert!(dominated, "vertex {v} not dominated in path4");
}
}
#[test]
fn test_dominating_set_empty() {
let ds = minimum_dominating_set(&[], 0);
assert!(ds.is_empty());
}
#[test]
fn test_vertex_cover_triangle() {
let vc = minimum_vertex_cover(&triangle(), 3);
let vc_set: HashSet<usize> = vc.iter().copied().collect();
for &(u, v) in &triangle() {
assert!(
vc_set.contains(&u) || vc_set.contains(&v),
"edge ({u},{v}) not covered"
);
}
}
#[test]
fn test_vertex_cover_path4() {
let vc = minimum_vertex_cover(&path4(), 4);
let vc_set: HashSet<usize> = vc.iter().copied().collect();
for &(u, v) in &path4() {
assert!(vc_set.contains(&u) || vc_set.contains(&v));
}
}
#[test]
fn test_vertex_cover_empty_edges() {
let vc = minimum_vertex_cover(&[], 5);
assert!(vc.is_empty());
}
#[test]
fn test_independent_set_triangle() {
let mis = maximum_independent_set(&triangle(), 3);
assert!(!mis.is_empty());
let mis_set: HashSet<usize> = mis.iter().copied().collect();
for &(u, v) in &triangle() {
assert!(
!(mis_set.contains(&u) && mis_set.contains(&v)),
"edge ({u},{v}) violates independence"
);
}
}
#[test]
fn test_independent_set_path4() {
let mis = maximum_independent_set(&path4(), 4);
let mis_set: HashSet<usize> = mis.iter().copied().collect();
for &(u, v) in &path4() {
assert!(!(mis_set.contains(&u) && mis_set.contains(&v)));
}
assert!(mis.len() >= 2);
}
#[test]
fn test_independent_set_k4() {
let mis = maximum_independent_set(&k4(), 4);
let mis_set: HashSet<usize> = mis.iter().copied().collect();
for &(u, v) in &k4() {
assert!(!(mis_set.contains(&u) && mis_set.contains(&v)));
}
assert_eq!(mis.len(), 1);
}
#[test]
fn test_edge_dominating_set_triangle() {
let eds = minimum_edge_dominating_set(&triangle(), 3);
assert!(!eds.is_empty());
let edge_set: HashSet<(usize, usize)> = eds.iter().copied().collect();
for &(u, v) in &triangle() {
let e = if u < v { (u, v) } else { (v, u) };
let dominated = edge_set.contains(&e)
|| edge_set.iter().any(|&(a, b)| a == u || b == u || a == v || b == v);
assert!(dominated, "edge ({u},{v}) not dominated");
}
}
#[test]
fn test_edge_dominating_set_empty() {
assert!(minimum_edge_dominating_set(&[], 3).is_empty());
}
#[test]
fn test_feedback_vertex_set_triangle() {
let fvs = feedback_vertex_set(&triangle(), 3);
let removed: Vec<bool> = (0..3).map(|v| fvs.contains(&v)).collect();
let adj = build_adj(&triangle(), 3);
assert!(
find_cycle(&adj, 3, &removed).is_none(),
"FVS should eliminate all cycles"
);
}
#[test]
fn test_feedback_vertex_set_tree_empty() {
let fvs = feedback_vertex_set(&path4(), 4);
assert!(fvs.is_empty(), "path has no cycles, FVS should be empty");
}
#[test]
fn test_feedback_vertex_set_k4() {
let fvs = feedback_vertex_set(&k4(), 4);
let removed: Vec<bool> = (0..4).map(|v| fvs.contains(&v)).collect();
let adj = build_adj(&k4(), 4);
assert!(
find_cycle(&adj, 4, &removed).is_none(),
"FVS of K4 should eliminate all cycles"
);
}
}