summary refs log tree commit diff homepage
path: root/2017/src/bin/day07.rs
blob: f579cbcc39e00a2dcaa40f710ee5739a4b03f27d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use std::cell::RefCell;
use std::collections::HashMap;
use std::io::{self, Read};
use std::rc::Rc;

#[derive(Debug, Default, Clone)]
struct Program {
    weight: u32,
    disc: Vec<Rc<RefCell<Program>>>,
}

impl Program {
    fn total_weight(&self) -> u32 {
        self.weight + self.disc.iter().map(|p| p.borrow().total_weight()).sum::<u32>()
    }

    fn balance(&self) -> Option<u32> {
        for child in &self.disc {
            match child.borrow().balance() {
                Some(x) => return Some(x),
                None => (),
            }
        }
        if self.disc.is_empty() {
            return None;
        }

        let mut disc = self.disc.clone();
        disc.sort_by_key(|p| p.borrow().total_weight());
        let max = disc.iter().map(|p| p.borrow().total_weight()).max().unwrap();
        let min = disc.iter().map(|p| p.borrow().total_weight()).min().unwrap();
        let avg = disc.iter().map(|p| p.borrow().total_weight()).sum::<u32>() / disc.len() as u32;
        if min == max {
            return None;
        } else if avg - min < max - avg {
            let child = disc.last().unwrap().borrow();
            let diff = child.total_weight() - min;
            return Some(child.weight - diff);
        } else {
            let child = disc.first().unwrap().borrow();
            let diff = max - child.total_weight();
            return Some(child.weight + diff);
        }
    }
}

fn solve1(input: &str) -> (String, Rc<RefCell<Program>>) {
    let mut programs: HashMap<String, Rc<RefCell<Program>>> = HashMap::new();
    for line in input.lines() {
        let mut words = line.split_whitespace();

        let name = words.next()
            .unwrap()
            .to_owned();
        let weight = words.next()
            .unwrap()
            .trim_matches(&['(', ')'][..])
            .parse()
            .unwrap();

        if words.next().is_none() {
            programs.entry(name)
                .or_insert_with(Default::default)
                .borrow_mut()
                .weight = weight;
            continue;
        }

        let disc = words.map(|child| {
            programs.entry(child.trim_right_matches(',').to_owned())
                .or_insert_with(Default::default)
                .clone()
        }).collect();

        let mut program = programs.entry(name)
            .or_insert_with(Default::default)
            .borrow_mut();
        program.weight = weight;
        program.disc = disc;
    }
    programs.into_iter()
        .find(|&(_, ref p)| Rc::strong_count(p) == 1)
        .unwrap()
}

fn solve2(input: &str) -> u32 {
    let root = solve1(input).1;
    let balance = root.borrow().balance().unwrap();
    balance
}

fn main() {
    let mut input = String::new();
    io::stdin().read_to_string(&mut input).unwrap();

    println!("Part 1: {}", solve1(&input).0);
    println!("Part 2: {}", solve2(&input));
}

#[test]
fn part1() {
    assert_eq!("tknk", solve1(
"\
pbga (66)
xhth (57)
ebii (61)
havc (66)
ktlj (57)
fwft (72) -> ktlj, cntj, xhth
qoyq (66)
padx (45) -> pbga, havc, qoyq
tknk (41) -> ugml, padx, fwft
jptl (61)
ugml (68) -> gyxo, ebii, jptl
gyxo (61)
cntj (57)
"
    ).0);
}

#[test]
fn part2() {
    assert_eq!(60, solve2(
"\
pbga (66)
xhth (57)
ebii (61)
havc (66)
ktlj (57)
fwft (72) -> ktlj, cntj, xhth
qoyq (66)
padx (45) -> pbga, havc, qoyq
tknk (41) -> ugml, padx, fwft
jptl (61)
ugml (68) -> gyxo, ebii, jptl
gyxo (61)
cntj (57)
"
    ));
}
:43 -0400'>2021-06-09Use seprintf for reserializeJune McEnroe 2021-06-09Use seprintf for capListJune McEnroe 2021-06-09Add seprintfJune McEnroe 2021-05-27Add pounce-notify to README 2.4June McEnroe 2021-05-27Fix ENVIRONMENT formatting in pounce-notify(1)June McEnroe 2021-05-27Add note about Libera.Chat SASL-only rangesJune McEnroe 2021-05-25Add QUIRKS fileJune McEnroe 2021-05-19Replace freenode with tilde.chatJune McEnroe 2021-05-04notify: Reword pounce-notify manualJune McEnroe 2021-05-02Clean up Makefiles, configure scriptsJune McEnroe 2021-04-30palaver: Exit on getopt failureJune McEnroe 2021-04-30notify: Implement pounce-notifyJune McEnroe