day5, part2

This commit is contained in:
Joe Ardent 2024-12-23 13:00:41 -08:00
parent aaae2a326c
commit 35ba924c1b
4 changed files with 194 additions and 1 deletions

7
Cargo.lock generated
View file

@ -24,6 +24,13 @@ dependencies = [
name = "day04" name = "day04"
version = "0.1.0" version = "0.1.0"
[[package]]
name = "day05"
version = "0.1.0"
dependencies = [
"winnow",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.4" version = "2.7.4"

View file

@ -1,6 +1,6 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
members = ["day01", "day02", "day03", "day04"] members = ["day01", "day02", "day03", "day04", "day05"]
[workspace.dependencies] [workspace.dependencies]
winnow = "0.6" winnow = "0.6"

7
day05/Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "day05"
version = "0.1.0"
edition = "2024"
[dependencies]
winnow.workspace = true

179
day05/src/main.rs Normal file
View file

@ -0,0 +1,179 @@
use std::collections::{HashMap, HashSet};
use winnow::{
PResult, Parser,
ascii::{dec_uint, newline},
combinator::{separated, separated_pair},
error::ContextError,
};
fn main() {
let input = std::fs::read_to_string("input").unwrap();
println!("{}", pt1(&input));
println!("{}", pt2(&input));
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct Rule(u32, u32);
type Rules = Vec<Rule>;
type Updates = Vec<Vec<u32>>;
type Graph = HashMap<u32, HashSet<u32>>;
fn pt1(input: &str) -> u32 {
let mut input = input;
let (rules, updates) = parse(&mut input).unwrap();
let mut total = 0;
for update in updates {
if check(&rules, &update) {
let mid = update.len() / 2;
total += update[mid];
}
}
total
}
fn check(rules: &Rules, update: &[u32]) -> bool {
let mut pages = HashMap::new();
for (i, p) in update.iter().enumerate() {
pages.insert(p, i);
}
for rule in rules {
let Rule(before, after) = rule;
if let Some(u) = pages.get(before) {
if let Some(v) = pages.get(after) {
if v <= u {
return false;
}
}
}
}
true
}
fn pt2(input: &str) -> u32 {
let mut input = input;
let mut total = 0;
let (rules, updates) = parse(&mut input).unwrap();
let mut neighbors = Graph::new();
for rule in &rules {
let Rule(u, v) = rule;
neighbors.entry(*u).or_default().insert(*v);
}
for update in updates {
if !check(&rules, &update) {
let sorted = tsort(&neighbors, &update);
let mid = sorted.len() / 2;
total += sorted[mid];
}
}
total
}
fn tsort(graph: &Graph, nodes: &[u32]) -> Vec<u32> {
let mut sorted = Vec::with_capacity(nodes.len());
let mut processed = HashSet::with_capacity(nodes.len());
let mut q = Vec::new();
let nodes: HashSet<u32> = nodes.iter().copied().collect();
for node in nodes.iter() {
q.push(*node);
while let Some(current) = q.pop() {
if !processed.contains(&current) {
q.push(current);
}
let mut do_top = true;
if let Some(nexts) = graph.get(&current) {
for n in nexts.iter().filter(|v| nodes.contains(v) && **v != current) {
if !processed.contains(n) {
q.push(*n);
do_top = false;
}
}
}
if do_top {
let _ = q.pop();
if processed.insert(current) {
sorted.push(current);
}
}
}
}
sorted
}
fn p_rule(input: &mut &str) -> PResult<Rule> {
let (a, b) = separated_pair(dec_uint, '|', dec_uint).parse_next(input)?;
Ok(Rule(a, b))
}
fn p_update(input: &mut &str) -> PResult<Vec<u32>> {
separated(1.., dec_uint::<&str, u32, ContextError>, ',').parse_next(input)
}
fn p_input(input: &mut &str) -> PResult<(Vec<Rule>, Updates)> {
separated_pair(
separated(1.., p_rule, newline),
(newline, newline),
separated(1.., p_update, newline),
)
.parse_next(input)
}
fn parse(input: &mut &str) -> PResult<(Rules, Updates)> {
let (rules, updates) = p_input(input)?;
Ok((rules, updates))
}
#[cfg(test)]
mod test {
use super::*;
static INPUT: &str = "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";
#[test]
fn p1() {
let v = pt1(INPUT);
assert_eq!(v, 143)
}
#[test]
fn p2() {
assert_eq!(123, pt2(INPUT));
}
}