use crate::core::{Graph, IgraphResult, VertexId};
pub fn is_dominating_set(graph: &Graph, dom_set: &[VertexId]) -> bool {
let n = graph.vcount() as usize;
if n == 0 {
return true;
}
let mut dominated = vec![false; n];
let directed = graph.is_directed();
for &v in dom_set {
if (v as usize) >= n {
continue;
}
dominated[v as usize] = true;
if let Ok(neighbors) = graph.neighbors(v) {
for &w in &neighbors {
dominated[w as usize] = true;
}
}
if directed {
if let Ok(in_neighbors) = graph.in_neighbors_vec(v) {
for &w in &in_neighbors {
dominated[w as usize] = true;
}
}
}
}
dominated.iter().all(|&d| d)
}
#[allow(clippy::cast_possible_truncation)] pub fn minimum_dominating_set(graph: &Graph) -> IgraphResult<Vec<VertexId>> {
let n = graph.vcount();
let directed = graph.is_directed();
if n == 0 {
return Ok(Vec::new());
}
let mut dominated = vec![false; n as usize];
let mut undominated_count = n;
let mut result = Vec::new();
let mut closed_neighborhoods: Vec<Vec<VertexId>> = Vec::with_capacity(n as usize);
for v in 0..n {
let mut cn = vec![v];
let out_n = graph.neighbors(v)?;
for &w in &out_n {
if !cn.contains(&w) {
cn.push(w);
}
}
if directed {
let in_n = graph.in_neighbors_vec(v)?;
for &w in &in_n {
if !cn.contains(&w) {
cn.push(w);
}
}
}
closed_neighborhoods.push(cn);
}
while undominated_count > 0 {
let mut best_v = 0u32;
let mut best_gain = 0u32;
for v in 0..n {
if dominated[v as usize] {
}
let gain = closed_neighborhoods[v as usize]
.iter()
.filter(|&&w| !dominated[w as usize])
.count() as u32;
if gain > best_gain || (gain == best_gain && v < best_v) {
best_gain = gain;
best_v = v;
}
}
if best_gain == 0 {
break;
}
result.push(best_v);
for &w in &closed_neighborhoods[best_v as usize] {
if !dominated[w as usize] {
dominated[w as usize] = true;
undominated_count -= 1;
}
}
}
result.sort_unstable();
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph() {
let g = Graph::with_vertices(0);
let ds = minimum_dominating_set(&g).unwrap();
assert!(ds.is_empty());
assert!(is_dominating_set(&g, &ds));
}
#[test]
fn single_vertex() {
let g = Graph::with_vertices(1);
let ds = minimum_dominating_set(&g).unwrap();
assert_eq!(ds.len(), 1);
assert!(is_dominating_set(&g, &ds));
}
#[test]
fn no_edges() {
let g = Graph::with_vertices(5);
let ds = minimum_dominating_set(&g).unwrap();
assert_eq!(ds.len(), 5);
assert!(is_dominating_set(&g, &ds));
}
#[test]
fn single_edge() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
let ds = minimum_dominating_set(&g).unwrap();
assert_eq!(ds.len(), 1);
assert!(is_dominating_set(&g, &ds));
}
#[test]
fn path_5() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 3).unwrap();
g.add_edge(3, 4).unwrap();
let ds = minimum_dominating_set(&g).unwrap();
assert!(is_dominating_set(&g, &ds));
assert!(ds.len() <= 3); }
#[test]
fn triangle() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 0).unwrap();
let ds = minimum_dominating_set(&g).unwrap();
assert!(is_dominating_set(&g, &ds));
assert_eq!(ds.len(), 1);
}
#[test]
fn star_graph() {
let mut g = Graph::with_vertices(5);
for i in 1..5u32 {
g.add_edge(0, i).unwrap();
}
let ds = minimum_dominating_set(&g).unwrap();
assert!(is_dominating_set(&g, &ds));
assert_eq!(ds.len(), 1); }
#[test]
fn complete_k4() {
let mut g = Graph::with_vertices(4);
for u in 0..4u32 {
for v in (u + 1)..4 {
g.add_edge(u, v).unwrap();
}
}
let ds = minimum_dominating_set(&g).unwrap();
assert!(is_dominating_set(&g, &ds));
assert_eq!(ds.len(), 1);
}
#[test]
fn bipartite_k22() {
let mut g = Graph::with_vertices(4);
g.add_edge(0, 2).unwrap();
g.add_edge(0, 3).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(1, 3).unwrap();
let ds = minimum_dominating_set(&g).unwrap();
assert!(is_dominating_set(&g, &ds));
assert!(ds.len() <= 2);
}
#[test]
fn directed_graph() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let ds = minimum_dominating_set(&g).unwrap();
assert!(is_dominating_set(&g, &ds));
}
#[test]
fn self_loop() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 0).unwrap();
g.add_edge(1, 2).unwrap();
let ds = minimum_dominating_set(&g).unwrap();
assert!(is_dominating_set(&g, &ds));
}
#[test]
fn isolated_with_edges() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 1).unwrap();
g.add_edge(2, 3).unwrap();
let ds = minimum_dominating_set(&g).unwrap();
assert!(is_dominating_set(&g, &ds));
assert!(ds.contains(&4)); }
#[test]
fn sorted_output() {
let mut g = Graph::with_vertices(6);
g.add_edge(4, 5).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(2, 3).unwrap();
let ds = minimum_dominating_set(&g).unwrap();
for i in 1..ds.len() {
assert!(ds[i] > ds[i - 1]);
}
}
#[test]
fn validator_positive() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 3).unwrap();
g.add_edge(3, 4).unwrap();
assert!(is_dominating_set(&g, &[1, 3]));
assert!(is_dominating_set(&g, &[0, 2, 4]));
assert!(is_dominating_set(&g, &[0, 1, 2, 3, 4]));
}
#[test]
fn validator_negative() {
let mut g = Graph::with_vertices(5);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 3).unwrap();
g.add_edge(3, 4).unwrap();
assert!(!is_dominating_set(&g, &[]));
assert!(!is_dominating_set(&g, &[0, 4])); assert!(!is_dominating_set(&g, &[2])); }
#[test]
fn petersen_graph() {
let mut g = Graph::with_vertices(10);
for i in 0..5u32 {
g.add_edge(i, (i + 1) % 5).unwrap();
}
for i in 0..5u32 {
g.add_edge(5 + i, 5 + (i + 2) % 5).unwrap();
}
for i in 0..5u32 {
g.add_edge(i, 5 + i).unwrap();
}
let ds = minimum_dominating_set(&g).unwrap();
assert!(is_dominating_set(&g, &ds));
assert!(ds.len() <= 5); }
#[test]
fn directed_domination_both_directions() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(2, 1).unwrap();
assert!(is_dominating_set(&g, &[1]));
}
}