From 688846610526465bb8ddd0d1fa6823988c9003a1 Mon Sep 17 00:00:00 2001 From: Keenan Tims Date: Wed, 3 Dec 2025 00:46:44 -0800 Subject: [PATCH] day3: add clever solution --- src/day3.rs | 53 +++++++++++++++++++++++++++++++------------ utils/misc/src/lib.rs | 20 +++++++++++++--- 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/src/day3.rs b/src/day3.rs index 2be5275..a8f7b4a 100644 --- a/src/day3.rs +++ b/src/day3.rs @@ -1,5 +1,6 @@ use aoc_runner_derive::{aoc, aoc_generator}; use memoize::memoize; +use misc::POW10; #[aoc_generator(day3)] fn parse(input: &str) -> Vec> { @@ -17,33 +18,47 @@ fn max_joltage(bank: Vec, n: usize) -> u64 { (0..bank.len() - n + 1) .map(|start| { - bank[start] as u64 * 10u64.pow(n as u32 - 1) - + max_joltage(bank[start + 1..].to_vec(), n - 1) + bank[start] as u64 * POW10[n - 1] + max_joltage(bank[start + 1..].to_vec(), n - 1) }) .max() .unwrap() - - // let mut max = 0; - // for start in 0..bank.len() - n + 1 { - // let cur = bank[start] as u64 * 10u64.pow(n as u32 - 1) - // + *bank[start + 1..].iter().max().unwrap() as u64; - // if cur > max { - // max = cur - // } - // } - // max } -#[aoc(day3, part1)] +fn max_joltage_2(bank: &[u8], n: usize) -> u64 { + if n == 1 { + return *bank.iter().max().unwrap() as u64; + } + let choices = &bank[..bank.len() - (n - 1)]; + + // Note: can't use position_max() here since it returns the _last_ instance and we need the _first_. + // get the max, then find the first instance of that value. There is definitely a one-pass solution. + let max = choices.iter().max().unwrap(); + let choice = choices.iter().position(|n| max == n).unwrap(); + let rest = &bank[choice + 1..]; + + bank[choice] as u64 * POW10[n - 1] + max_joltage_2(rest, n - 1) +} + +#[aoc(day3, part1, Brute)] fn part1(input: &[Vec]) -> u64 { input.iter().map(|bank| max_joltage(bank.clone(), 2)).sum() } -#[aoc(day3, part2)] +#[aoc(day3, part2, Brute)] fn part2(input: &[Vec]) -> u64 { input.iter().map(|bank| max_joltage(bank.clone(), 12)).sum() } +#[aoc(day3, part1, Smart)] +fn part1_smart(input: &[Vec]) -> u64 { + input.iter().map(|bank| max_joltage_2(bank, 2)).sum() +} + +#[aoc(day3, part2, Smart)] +fn part2_smart(input: &[Vec]) -> u64 { + input.iter().map(|bank| max_joltage_2(bank, 12)).sum() +} + #[cfg(test)] mod tests { use super::*; @@ -62,4 +77,14 @@ mod tests { fn part2_example() { assert_eq!(part2(&parse(EXAMPLE)), 3121910778619); } + + #[test] + fn part1_smart_example() { + assert_eq!(part1_smart(&parse(EXAMPLE)), 357); + } + + #[test] + fn part2_smart_example() { + assert_eq!(part2_smart(&parse(EXAMPLE)), 3121910778619); + } } diff --git a/utils/misc/src/lib.rs b/utils/misc/src/lib.rs index b7d4b5c..46d447f 100644 --- a/utils/misc/src/lib.rs +++ b/utils/misc/src/lib.rs @@ -2,6 +2,19 @@ use num_traits::Signed; use std::fmt::Display; use std::ops::{Add, AddAssign}; +const POW10MAX: usize = u64::MAX.ilog10() as usize; +pub const POW10: [u64; POW10MAX] = pow10_lut(); + +const fn pow10_lut() -> [u64; N] { + let mut res = [0; N]; + let mut i = 0; + while i < N { + res[i] = 10u64.pow(i as u32); + i += 1; + } + res +} + /// Wrapped signed integer with custom upper bound with wrapping of 0s to the upper bound #[derive(Eq, Clone, Copy)] pub struct CustomWrapped { @@ -91,8 +104,9 @@ mod tests { use super::*; #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + fn test_pow10() { + for i in 0..POW10MAX { + assert_eq!(POW10[i], 10u64.pow(i as u32)) + } } }