813-minimum-vertex-cover — Minimum Vertex Cover (Trees)
Tutorial Video
Text description (accessibility)
This video demonstrates the "813-minimum-vertex-cover — Minimum Vertex Cover (Trees)" functional Rust example. Difficulty level: Intermediate. Key concepts covered: Functional Programming. A vertex cover is a set of vertices such that every edge has at least one endpoint in the set. Key difference from OCaml: 1. **Tree DP structure**: Both languages implement the same O(n) tree DP; the recursion pattern is nearly identical.
Tutorial
The Problem
A vertex cover is a set of vertices such that every edge has at least one endpoint in the set. The minimum vertex cover finds the smallest such set. For general graphs this is NP-hard (equivalent to maximum independent set), but for trees it is solvable in O(n) by DP on the tree structure. Applications: network security (placing sensors to monitor all links), database index optimization, and approximation algorithms for general graphs.
🎯 Learning Outcomes
dp[v][0] (v not in cover) and dp[v][1] (v in cover)Code Example
#![allow(clippy::all)]
//! # Minimum Vertex Cover (Trees)
pub fn min_vertex_cover_tree(n: usize, edges: &[(usize, usize)]) -> usize {
if n == 0 {
return 0;
}
let mut adj = vec![vec![]; n];
for &(u, v) in edges {
adj[u].push(v);
adj[v].push(u);
}
let mut dp = vec![[0, 0]; n];
let mut visited = vec![false; n];
fn dfs(v: usize, adj: &[Vec<usize>], dp: &mut [[usize; 2]], vis: &mut [bool]) {
vis[v] = true;
dp[v][0] = 0;
dp[v][1] = 1;
for &u in &adj[v] {
if !vis[u] {
dfs(u, adj, dp, vis);
dp[v][0] += dp[u][1];
dp[v][1] += dp[u][0].min(dp[u][1]);
}
}
}
dfs(0, &adj, &mut dp, &mut visited);
dp[0][0].min(dp[0][1])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vc() {
assert_eq!(min_vertex_cover_tree(4, &[(0, 1), (1, 2), (1, 3)]), 1);
}
}Key Differences
vis array, OCaml passes parent explicitly.OCaml Approach
OCaml implements tree DP with Array.make n [|0; 0|] and recursive DFS. let rec dfs v parent = ... List.iter (fun u -> if u <> parent then (dfs u v; ...)) adj.(v). The DP update is: dp.(v).(0) <- dp.(v).(0) + dp.(u).(1) and dp.(v).(1) <- dp.(v).(1) + min dp.(u).(0) dp.(u).(1). OCaml's pattern matching makes the two cases readable.
Full Source
#![allow(clippy::all)]
//! # Minimum Vertex Cover (Trees)
pub fn min_vertex_cover_tree(n: usize, edges: &[(usize, usize)]) -> usize {
if n == 0 {
return 0;
}
let mut adj = vec![vec![]; n];
for &(u, v) in edges {
adj[u].push(v);
adj[v].push(u);
}
let mut dp = vec![[0, 0]; n];
let mut visited = vec![false; n];
fn dfs(v: usize, adj: &[Vec<usize>], dp: &mut [[usize; 2]], vis: &mut [bool]) {
vis[v] = true;
dp[v][0] = 0;
dp[v][1] = 1;
for &u in &adj[v] {
if !vis[u] {
dfs(u, adj, dp, vis);
dp[v][0] += dp[u][1];
dp[v][1] += dp[u][0].min(dp[u][1]);
}
}
}
dfs(0, &adj, &mut dp, &mut visited);
dp[0][0].min(dp[0][1])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vc() {
assert_eq!(min_vertex_cover_tree(4, &[(0, 1), (1, 2), (1, 3)]), 1);
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vc() {
assert_eq!(min_vertex_cover_tree(4, &[(0, 1), (1, 2), (1, 3)]), 1);
}
}
Deep Comparison
OCaml vs Rust: Minimum Vertex Cover
Overview
See the example.rs and example.ml files for detailed implementations.
Key Differences
| Aspect | OCaml | Rust |
|---|---|---|
| Type system | Hindley-Milner | Ownership + traits |
| Memory | GC | Zero-cost abstractions |
| Mutability | Explicit ref | mut keyword |
| Error handling | Option/Result | Result<T, E> |
See README.md for detailed comparison.
Exercises
min_vertex_cover_reconstruct(n, edges) -> Vec<usize> that returns the actual vertices in the cover, not just the count.MIS = n - MVC, use the tree DP to compute both. Verify MVC + MIS = n.