11//! # Knights of the Dinner Table
22//!
33//! 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 permuting the order of the diners.
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.
58//!
6- //! For part one we reduce the permutations from 8! = 40,320 permutations to 7!/2 = 2,520
7- //! permutations by arbitrarily choosing one of the diners as the start, and skipping lexically
8- //! reversed forms (seating a->b->c->a gives the same happiness as seating c->b->a->c).
9- //!
10- //! We solve part two at the same time by noticing that by inserting yourself between two diners
11- //! you set the value of their mutual link to zero. Keeping track of the weakest link
12- //! then subtracting that from the value for part one gives the result for part two at almost
13- //! no additional cost .
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 .
1417//!
1518//! [`Day 9`]: crate::year2015::day09
19+ //! [Held-Karp]: https://en.wikipedia.org/wiki/Held%E2%80%93Karp_algorithm
20+ use crate :: util:: bitset:: * ;
1621use crate :: util:: hash:: * ;
1722use crate :: util:: iter:: * ;
1823use crate :: util:: parse:: * ;
19- use crate :: util:: slice:: * ;
2024
21- type Input = ( i32 , i32 ) ;
25+ type Input = ( i16 , i16 ) ;
2226
2327pub fn parse ( input : & str ) -> Input {
2428 // Assign each diner an index on a first come first served basis.
@@ -35,52 +39,73 @@ pub fn parse(input: &str) -> Input {
3539
3640 // Calculate the happiness values. Note that the values are not reciprocal a => b != b => a.
3741 let stride = indices. len ( ) ;
38- let mut happiness = vec ! [ 0 ; stride * stride] ;
42+ let mut happiness = vec ! [ 0_i16 ; stride * stride] ;
3943
4044 for & [ from, _, gain_lose, value, .., to, _] in & tokens {
4145 let start = indices[ from] ;
4246 let end = indices[ to] ;
4347 let sign = if gain_lose == "gain" { 1 } else { -1 } ;
44- let value: i32 = value. signed ( ) ;
48+ let value: i16 = value. signed ( ) ;
4549
4650 // Add the values together to make the mutual link reciprocal.
4751 happiness[ stride * start + end] += sign * value;
4852 happiness[ stride * end + start] += sign * value;
4953 }
5054
5155 // Solve both parts simultaneously.
52- let mut part_one = 0 ;
53- let mut part_two = 0 ;
54- let mut indices: Vec < _ > = ( 1 ..stride) . collect ( ) ;
55-
56- indices. half_permutations ( |slice| {
57- let mut sum = 0 ;
58- let mut weakest_link = i32:: MAX ;
59-
60- let mut link = |from, to| {
61- let value = happiness[ stride * from + to] ;
62- sum += value;
63- weakest_link = weakest_link. min ( value) ;
64- } ;
56+ // Initialize a shared table for both parts: 2ⁿ sets with n distances per set. Default 0 matches
57+ // g({k},k) for all singleton sets of zero distance from yourself, but tracking k as the start
58+ // of the path. The initial value of other sets does not matter.
59+ let zero = ( 0_i16 , 0_u8 ) ;
60+ let mut table = vec ! [ zero; stride * ( 1 << stride) ] ;
61+ for k in 0 ..stride {
62+ table[ ( 1 << k) * stride + k] . 1 = k as u8 ;
63+ }
6564
66- link ( 0 , slice[ 0 ] ) ;
67- link ( 0 , slice[ slice. len ( ) - 1 ] ) ;
65+ // Visit each non-empty set in order, with no work to do for singleton sets.
66+ for set in 3_usize ..( 1 << stride) {
67+ if set & !( set - 1 ) == set {
68+ continue ;
69+ }
6870
69- for i in 1 ..slice. len ( ) {
70- link ( slice[ i] , slice[ i - 1 ] ) ;
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) ;
7189 }
90+ }
7291
73- part_one = part_one. max ( sum) ;
74- part_two = part_two. max ( sum - weakest_link) ;
75- } ) ;
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+ }
76101
77102 ( part_one, part_two)
78103}
79104
80- pub fn part1 ( input : & Input ) -> i32 {
105+ pub fn part1 ( input : & Input ) -> i16 {
81106 input. 0
82107}
83108
84- pub fn part2 ( input : & Input ) -> i32 {
109+ pub fn part2 ( input : & Input ) -> i16 {
85110 input. 1
86111}
0 commit comments