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
/* Copyright 2023 Mario Finelli
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! Advent of Code 2015 Day 9: <https://adventofcode.com/2015/day/9>
//!
//! This challenge turned out to be pretty easy. The list of inputs is small
//! so we don't need to worry about building any graphs and then trying to
//! find the shortest path or anything. We can just compute all of the possible
//! trips and their distances and then get the shortest/longest.
use itertools::Itertools;
use std::cmp::Reverse;
use std::collections::{BinaryHeap, HashMap};
/// The solution for the day nine challenge.
///
/// We take the input as a string and the problem part as an integer as usual.
/// We start by iterating over the lines and parsing the city names and the
/// distances. We maintain a [`std::collections::HashMap`] of the city names
/// with their values equal to another HashMap that contains the other cities
/// and their distances. This way we can lookup the distance from one city to
/// any other city. Then we get all of the permutations of the cities which
/// gives us all of the possible trips that we could take and then look at them
/// by two to give us each leg of the trip. Just get the distance for the trip
/// and then add it to the [`std::collections::BinaryHeap`]s that we've been
/// maintaining of the trip distances. If we're on part one then get the value
/// from the min-heap, otherwise the value from the max-heap.
///
/// # Example
/// ```rust
/// # use aoc::y15d09::y15d09;
/// // probably read this from the input file...
/// let input = "A to B = 10\nB to C = 15\nA to C = 25\n";
/// assert_eq!(y15d09(input, 1), 25);
/// assert_eq!(y15d09(input, 2), 40);
/// ```
pub fn y15d09(input: &str, part: u32) -> u32 {
let lines: Vec<_> = input.lines().collect();
let mut distances: HashMap<&str, HashMap<&str, u32>> = HashMap::new();
let mut shortest_trips = BinaryHeap::new();
let mut longest_trips = BinaryHeap::new();
for line in lines {
let text: Vec<&str> = line.split_whitespace().collect();
let city1 = text[0];
let city2 = text[2];
let distance: u32 = text[4].parse().unwrap();
let city = distances.entry(city1).or_default();
city.insert(city2, distance);
let city = distances.entry(city2).or_default();
city.insert(city1, distance);
}
let paths = distances.keys().permutations(distances.keys().len());
for path in paths {
let mut distance = 0;
for trip in path.windows(2) {
distance += distances.get(trip[0]).unwrap().get(trip[1]).unwrap();
}
shortest_trips.push(Reverse(distance));
longest_trips.push(distance);
}
if part == 1 {
let Reverse(shortest) = shortest_trips.pop().unwrap();
shortest
} else {
longest_trips.pop().unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
#[test]
fn it_works() {
let input = concat!(
"London to Dublin = 464\n",
"London to Belfast = 518\n",
"Dublin to Belfast = 141",
);
assert_eq!(y15d09(input, 1), 605);
assert_eq!(y15d09(input, 2), 982);
}
#[test]
fn the_solution() {
let contents = fs::read_to_string("input/2015/day09.txt").unwrap();
assert_eq!(y15d09(&contents, 1), 207);
assert_eq!(y15d09(&contents, 2), 804);
}
}