Skip to main content

aoc/year2021/
day07.rs

1//! # The Treachery of Whales
2//!
3//! Part one is a disguised definition of the mathematical [median](https://en.wikipedia.org/wiki/Median).
4//! We can calculate the result immediately using the standard algorithm. Even though there
5//! are an even number of crabs, any integer between the 500th and 501st crab inclusive will
6//! work (the extra fuel spent by half the crabs perfectly cancels the fuel saved by the other
7//! half, when switching between integers in that range).
8//!
9//! Part two is found by using the [mean](https://en.wikipedia.org/wiki/Mean).
10//! However, since this could be a floating point value and we are using integers we need to check
11//! both the floor and the ceiling of that result to ensure the correct answer.
12use crate::util::parse::*;
13
14pub fn parse(input: &str) -> Vec<i32> {
15    input.iter_signed().collect()
16}
17
18pub fn part1(input: &[i32]) -> i32 {
19    let median = median(input);
20    input.iter().map(|n| (n - median).abs()).sum()
21}
22
23pub fn part2(input: &[i32]) -> i32 {
24    let mean = mean(input);
25    let triangle = |x: i32, mean: i32| {
26        let n = (x - mean).abs();
27        (n * (n + 1)) / 2
28    };
29
30    (0..=1).map(|delta| input.iter().map(|&x| triangle(x, mean + delta)).sum()).min().unwrap()
31}
32
33fn median(input: &[i32]) -> i32 {
34    // A radix sort followed by a short-circuiting .position() would also work, but takes
35    // more lines of code without much more speed.
36    let mut crabs = input.to_vec();
37    let middle = crabs.len() / 2;
38    *crabs.select_nth_unstable(middle).1
39}
40
41fn mean(input: &[i32]) -> i32 {
42    let sum: i32 = input.iter().sum();
43    sum / (input.len() as i32)
44}