aoc/year2015/day13.rs
1//! # Knights of the Dinner Table
2//!
3//! This problem is very similar to [`Day 9`] and we solve it in almost exactly the same way by
4//! computing an adjacency matrix of happiness then running [Held-Karp] to find the longest
5//! cycle. If part one were the only problem at hand, the answer would be possible by iterating
6//! over 127 sets and then selecting among seven candidates to close the loop back to whichever
7//! abritrary point we pinned as the start.
8//!
9//! However, we are more interested in solving part two at the same time. Do this by noticing that
10//! when you insert yourself between two diners, you set the value of their mutual link to zero.
11//! This is effectively the same as inserting a ninth node into the algorithm, which we pin as the
12//! start node before iterating over 255 sets. Meanwhile, the results for part one can still be
13//! found from the table, if we also have an easy way to determine which diner was used to start
14//! the path represented by any given g(set,k). We can then manually close the loop of 8 diners
15//! by using all 8 g(255,k) plus the distance from k to the start node of that path, while the
16//! loop of 9 diners uses g(255,k) with no additional distance.
17//!
18//! [`Day 9`]: crate::year2015::day09
19//! [Held-Karp]: https://en.wikipedia.org/wiki/Held%E2%80%93Karp_algorithm
20use crate::util::bitset::*;
21use crate::util::hash::*;
22use crate::util::iter::*;
23use crate::util::parse::*;
24
25type Input = (i16, i16);
26
27pub fn parse(input: &str) -> Input {
28 // Assign each diner an index on a first come first served basis.
29 let tokens: Vec<_> = input.split([' ', '.', '\n']).chunk::<12>().collect();
30 let mut indices = FastMap::new();
31
32 for &[from, .., to, _] in &tokens {
33 for key in [from, to] {
34 let size = indices.len();
35 indices.entry(key).or_insert(size);
36 }
37 }
38
39 // Calculate the happiness values. Note that the values are not reciprocal a => b != b => a.
40 let stride = indices.len();
41 let mut happiness = vec![0_i16; stride * stride];
42
43 for &[from, _, gain_lose, value, .., to, _] in &tokens {
44 let start = indices[from];
45 let end = indices[to];
46 let sign = if gain_lose == "gain" { 1 } else { -1 };
47 let value: i16 = value.signed();
48
49 // Add the values together to make the mutual link reciprocal.
50 happiness[stride * start + end] += sign * value;
51 happiness[stride * end + start] += sign * value;
52 }
53
54 // Solve both parts simultaneously.
55 // Initialize a shared table for both parts: 2ⁿ sets with n distances per set. Default 0 matches
56 // g({k},k) for all singleton sets of zero distance from yourself, but tracking k as the start
57 // of the path. The initial value of other sets does not matter.
58 let zero = (0_i16, 0_u8);
59 let mut table = vec![zero; stride * (1 << stride)];
60 for k in 0..stride {
61 table[(1 << k) * stride + k].1 = k as u8;
62 }
63
64 // Visit each non-empty set in order, with no work to do for singleton sets. Start from 3,
65 // since 1 and 2 are singleton sets.
66 for set in 3_usize..(1 << stride) {
67 if set.is_power_of_two() {
68 continue;
69 }
70
71 // For a given set, compute each g(set,k) for all k in the set.
72 for k in set.biterator() {
73 let subset = set ^ (1 << k);
74 let mut longest = i16::MIN;
75 let mut start = u8::MAX;
76
77 // For a given destination k, find which other bit m gives the best path from the
78 // subset to m, and then m to k. All table[subset] references were filled in prior
79 // iterations of the outer loop or the singleton base cases.
80 for m in subset.biterator() {
81 let prior = table[subset * stride + m];
82 let distance = prior.0 + happiness[m * stride + k];
83 if distance > longest {
84 longest = distance;
85 start = prior.1;
86 }
87 }
88 table[set * stride + k] = (longest, start);
89 }
90 }
91
92 // With the sets now built, we have stride candidates for each answer.
93 // Part one requires completing the cycle back to the stashed first element.
94 // Part two can be directly read off the table.
95 let mut part_one = i16::MIN;
96 let mut part_two = i16::MIN;
97 for (k, &prior) in table[table.len() - stride..].iter().enumerate() {
98 part_one = part_one.max(prior.0 + happiness[prior.1 as usize * stride + k]);
99 part_two = part_two.max(prior.0);
100 }
101
102 (part_one, part_two)
103}
104
105pub fn part1(input: &Input) -> i16 {
106 input.0
107}
108
109pub fn part2(input: &Input) -> i16 {
110 input.1
111}