diff --git a/data/examples/05.txt b/data/examples/05.txt new file mode 100644 index 0000000..d4d4441 --- /dev/null +++ b/data/examples/05.txt @@ -0,0 +1,28 @@ +47|53 +97|13 +97|61 +97|47 +75|29 +61|13 +75|53 +29|13 +97|29 +53|29 +61|53 +97|53 +61|29 +47|13 +75|47 +97|75 +47|61 +75|61 +47|29 +75|13 +53|13 + +75,47,61,53,29 +97,61,53,29,13 +75,29,13 +75,97,47,61,53 +61,13,29 +97,13,75,29,47 \ No newline at end of file diff --git a/src/bin/05.rs b/src/bin/05.rs new file mode 100644 index 0000000..f71c408 --- /dev/null +++ b/src/bin/05.rs @@ -0,0 +1,158 @@ +use std::collections::{HashMap, HashSet, VecDeque}; + +advent_of_code::solution!(5); + +pub fn part_one(input: &str) -> Option { + let (rules_section, updates_section) = input.split_once("\n\n")?; + + let mut rules: Vec<(u32, u32)> = Vec::new(); + + for rule in rules_section.lines() { + let (x, y) = rule.split_once("|")?; + rules.push((x.parse().unwrap(), y.parse().unwrap())); + } + + let updates: Vec> = updates_section + .lines() + .map(|l| { + l.trim() + .split(',') + .map(|n| n.parse::().ok()) + .collect::>>() + }) + .collect::>>>()?; + + let mut sum = 0; + + for update in updates { + if is_ordered(&update, &rules) { + let m_idx = update.len() / 2; + let m_page = update[m_idx]; + sum += m_page; + } + } + + Some(sum) +} + +fn is_ordered(update: &[u32], rules: &[(u32, u32)]) -> bool { + let mut page_indices: HashMap = HashMap::new(); + for (i, page) in update.iter().enumerate() { + page_indices.insert(*page, i); + } + + for &(x, y) in rules { + if let (Some(x_idx), Some(y_idx)) = (page_indices.get(&x), page_indices.get(&y)) { + if x_idx >= y_idx { + return false; + } + } + } + + true +} + +pub fn part_two(input: &str) -> Option { + let (rules_section, updates_section) = input.split_once("\n\n")?; + + let mut rules: Vec<(u32, u32)> = Vec::new(); + + for rule in rules_section.lines() { + let (x, y) = rule.split_once("|")?; + rules.push((x.parse().unwrap(), y.parse().unwrap())); + } + + let updates: Vec> = updates_section + .lines() + .map(|l| { + l.trim() + .split(',') + .map(|n| n.parse::().ok()) + .collect::>>() + }) + .collect::>>>()?; + + let mut sum = 0; + + for update in updates { + if !is_ordered(&update, &rules) { + if let Some(corrected) = reorder(&update, &rules) { + let m_idx = corrected.len() / 2; + let m_page = corrected[m_idx]; + sum += m_page; + } else { + continue; + } + } + } + Some(sum) +} + +fn reorder(update: &[u32], rules: &[(u32, u32)]) -> Option> { + let mut graph: HashMap> = HashMap::new(); + let mut in_degree: HashMap = HashMap::new(); + + for &page in update { + graph.entry(page).or_default(); + in_degree.entry(page).or_insert(0); + } + + for &(before, after) in rules { + if update.contains(&before) && update.contains(&after) { + graph.entry(before).or_default().push(after); + *in_degree.entry(after).or_insert(0) += 1; + } + } + + let mut queue: VecDeque = VecDeque::new(); + for (&page, °ree) in &in_degree { + if degree == 0 { + queue.push_back(page); + } + } + + let mut sorted_pages = Vec::new(); + + while let Some(page) = queue.pop_front() { + sorted_pages.push(page); + + if let Some(neighbors) = graph.get(&page) { + for &neighbor in neighbors { + if let Some(degree) = in_degree.get_mut(&neighbor) { + *degree -= 1; + if *degree == 0 { + queue.push_back(neighbor); + } + } + } + } + } + + if sorted_pages.len() == update.len() { + let page_set: HashSet = update.iter().cloned().collect(); + let sorted_update: Vec = sorted_pages + .into_iter() + .filter(|page| page_set.contains(page)) + .collect(); + Some(sorted_update) + } else { + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_part_one() { + let result = part_one(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(143)); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(123)); + } +}