aoc/year2015/
day09.rs

1//! # All in a Single Night
2//!
3//! This is a variant of the classic NP-hard
4//! [Travelling Salesman Problem](https://en.wikipedia.org/wiki/Travelling_salesman_problem).
5//!
6//! There are 8 locations, so naively it would require checking 8! = 40,320 permutations. We can
7//! reduce this to 7!/2 = 2,520 permutations by arbitrarily choosing one of the locations as the
8//! start, and skipping lexically reversed permutations (since the path a->b->c has the same
9//! length as c->b->a).
10//!
11//! We then compute the distance to complete the trip and return to the original location.
12//! Since the problem does not ask us to end up in the same location we then "break" the cycle.
13//! To compute the shortest journey we remove the longest single journey and to compute the
14//! longest journey we remove the shortest single journey.
15//!
16//! For speed we first convert each location into an index, then store the distances between
17//! every pair of locations in an array for fast lookup. Our utility [`half_permutations`] method uses
18//! [Steinhaus-Johnson-Trotter's algorithm](https://en.wikipedia.org/wiki/Steinhaus%E2%80%93Johnson%E2%80%93Trotter_algorithm) for efficiency,
19//! modifying the slice in place.
20//!
21//! [`half_permutations`]: crate::util::slice
22use crate::util::hash::*;
23use crate::util::iter::*;
24use crate::util::parse::*;
25use crate::util::slice::*;
26
27type Result = (u32, u32);
28
29pub fn parse(input: &str) -> Result {
30    let tokens: Vec<_> = input.split_ascii_whitespace().chunk::<5>().collect();
31    let mut indices = FastMap::new();
32
33    for [start, _, end, ..] in &tokens {
34        let size = indices.len();
35        indices.entry(start).or_insert(size);
36
37        let size = indices.len();
38        indices.entry(end).or_insert(size);
39    }
40
41    let stride = indices.len();
42    let mut distances = vec![0; stride * stride];
43
44    for [start, _, end, _, distance] in &tokens {
45        let start = indices[start];
46        let end = indices[end];
47        let distance = distance.unsigned();
48
49        distances[stride * start + end] = distance;
50        distances[stride * end + start] = distance;
51    }
52
53    let mut global_min = u32::MAX;
54    let mut global_max = u32::MIN;
55    let mut indices: Vec<_> = (1..stride).collect();
56
57    indices.half_permutations(|slice| {
58        let mut sum = 0;
59        let mut local_min = u32::MAX;
60        let mut local_max = u32::MIN;
61
62        let mut trip = |from, to| {
63            let distance = distances[stride * from + to];
64            sum += distance;
65            local_min = local_min.min(distance);
66            local_max = local_max.max(distance);
67        };
68
69        // First trip.
70        trip(0, slice[0]);
71        // Last trip.
72        trip(0, slice[slice.len() - 1]);
73        // Intermediate trips.
74        for i in 1..slice.len() {
75            trip(slice[i], slice[i - 1]);
76        }
77
78        global_min = global_min.min(sum - local_max);
79        global_max = global_max.max(sum - local_min);
80    });
81
82    (global_min, global_max)
83}
84
85pub fn part1(input: &Result) -> u32 {
86    input.0
87}
88
89pub fn part2(input: &Result) -> u32 {
90    input.1
91}