day(16): add solution
Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
parent
f93a8db8f9
commit
c14c785355
2 changed files with 226 additions and 0 deletions
10
samples/day16.txt
Normal file
10
samples/day16.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
Valve AA has flow rate=0; tunnels lead to valves DD, II, BB
|
||||
Valve BB has flow rate=13; tunnels lead to valves CC, AA
|
||||
Valve CC has flow rate=2; tunnels lead to valves DD, BB
|
||||
Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE
|
||||
Valve EE has flow rate=3; tunnels lead to valves FF, DD
|
||||
Valve FF has flow rate=0; tunnels lead to valves EE, GG
|
||||
Valve GG has flow rate=0; tunnels lead to valves FF, HH
|
||||
Valve HH has flow rate=22; tunnel leads to valve GG
|
||||
Valve II has flow rate=0; tunnels lead to valves AA, JJ
|
||||
Valve JJ has flow rate=21; tunnel leads to valve II
|
216
src/bin/day16.rs
Normal file
216
src/bin/day16.rs
Normal file
|
@ -0,0 +1,216 @@
|
|||
use std::{
|
||||
cmp::{max, min, Reverse},
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
ops::Index,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use aoc_2022::*;
|
||||
use itertools::iproduct;
|
||||
|
||||
type Input = Graph;
|
||||
type Output = i32;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Vertex {
|
||||
rate: i32,
|
||||
next: Vec<String>,
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
fn new(rate: i32, next: Vec<String>) -> Vertex {
|
||||
Vertex { rate, next }
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref REGEX: Regex = Regex::new(
|
||||
r"Valve (?P<v>\w+) has flow rate=(?P<r>-?\d+); tunnel(s)? lead(s)? to valve(s)? (?P<next>.*)"
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Graph {
|
||||
g: BTreeMap<String, Vertex>,
|
||||
to_open: BTreeSet<String>,
|
||||
distances: BTreeMap<(String, String), i32>,
|
||||
}
|
||||
|
||||
impl FromStr for Graph {
|
||||
type Err = Report;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.trim_end();
|
||||
|
||||
let mut g: BTreeMap<String, Vertex> = BTreeMap::new();
|
||||
for line in s.lines() {
|
||||
let caps = REGEX.captures(line).unwrap();
|
||||
|
||||
let v = caps["v"].to_string();
|
||||
let r: i32 = caps["r"].parse()?;
|
||||
let next = caps["next"]
|
||||
.split(", ")
|
||||
.map(|n| n.to_string())
|
||||
.collect_vec();
|
||||
|
||||
g.insert(v, Vertex::new(r, next));
|
||||
}
|
||||
|
||||
Ok(Graph::new(g))
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<&str> for Graph {
|
||||
type Output = Vertex;
|
||||
|
||||
fn index(&self, index: &str) -> &Self::Output {
|
||||
&self.g[index]
|
||||
}
|
||||
}
|
||||
|
||||
fn floyd_warshall(g: &BTreeMap<String, Vertex>) -> BTreeMap<(String, String), i32> {
|
||||
let upper_bound = i32::MAX / 2;
|
||||
|
||||
let mut distances = BTreeMap::new();
|
||||
for (u, v) in iproduct!(g.keys(), g.keys()) {
|
||||
let is_neighbour = g[u].next.contains(v);
|
||||
distances.insert(
|
||||
(u.clone(), v.clone()),
|
||||
if is_neighbour { 1 } else { upper_bound },
|
||||
);
|
||||
}
|
||||
|
||||
for (w, u, v) in iproduct!(g.keys(), g.keys(), g.keys()) {
|
||||
let key = (u.clone(), v.clone());
|
||||
let current = *distances.get(&key).unwrap();
|
||||
|
||||
distances.insert(
|
||||
key,
|
||||
min(
|
||||
current,
|
||||
distances[&(u.clone(), w.clone())] + distances[&(w.clone(), v.clone())],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
distances
|
||||
}
|
||||
|
||||
impl Graph {
|
||||
fn new(g: BTreeMap<String, Vertex>) -> Self {
|
||||
let mut to_open: BTreeSet<String> = BTreeSet::new();
|
||||
for (id, v) in g.iter() {
|
||||
if v.rate > 0 {
|
||||
to_open.insert(id.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let distances = floyd_warshall(&g);
|
||||
|
||||
Self {
|
||||
g,
|
||||
to_open,
|
||||
distances,
|
||||
}
|
||||
}
|
||||
|
||||
fn distance(&self, u: &str, v: &str) -> i32 {
|
||||
self.distances[&(u.to_string(), v.to_string())]
|
||||
}
|
||||
}
|
||||
|
||||
fn max_flow(
|
||||
g: &Input,
|
||||
cache: &mut BTreeMap<String, Output>,
|
||||
valves: &mut BTreeSet<String>,
|
||||
start: &str,
|
||||
t: i32,
|
||||
) -> Output {
|
||||
let key = format!(
|
||||
"{}-{}-{}",
|
||||
t,
|
||||
start,
|
||||
valves
|
||||
.iter()
|
||||
.sorted_by_key(|&v| Reverse(g.g[v].rate))
|
||||
.join("-")
|
||||
);
|
||||
|
||||
if let Some(&flow) = cache.get(&key) {
|
||||
// we have already precomputed the flow
|
||||
return flow;
|
||||
}
|
||||
|
||||
let flow_from_start = g[start].rate * t;
|
||||
|
||||
let mut flow_from_next = 0;
|
||||
for v in valves.clone().iter().collect_vec() {
|
||||
let d = g.distance(start, v);
|
||||
|
||||
if t < d + 1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
valves.remove(v);
|
||||
flow_from_next = max(flow_from_next, max_flow(g, cache, valves, v, t - d - 1));
|
||||
valves.insert(v.clone());
|
||||
}
|
||||
|
||||
cache.insert(key, flow_from_start + flow_from_next);
|
||||
flow_from_start + flow_from_next
|
||||
}
|
||||
|
||||
fn pairings(
|
||||
valves: &BTreeSet<String>,
|
||||
) -> impl Iterator<Item = (BTreeSet<String>, BTreeSet<String>)> + '_ {
|
||||
let mapping = valves.iter().collect_vec();
|
||||
|
||||
let max_mask = 1 << (valves.len() - 1);
|
||||
|
||||
(0..max_mask).map(move |mask| {
|
||||
let mut elephant = BTreeSet::new();
|
||||
let mut human = BTreeSet::new();
|
||||
|
||||
for (i, &v) in mapping.iter().enumerate() {
|
||||
if (mask & (1 << i)) == 0 {
|
||||
human.insert(v.clone());
|
||||
} else {
|
||||
elephant.insert(v.clone());
|
||||
}
|
||||
}
|
||||
|
||||
(human, elephant)
|
||||
})
|
||||
}
|
||||
|
||||
struct Day16;
|
||||
impl Solution<Input, Output> for Day16 {
|
||||
fn parse_input<P: AsRef<Path>>(pathname: P) -> Input {
|
||||
file_to_string(pathname).parse().unwrap()
|
||||
}
|
||||
|
||||
fn part_1(input: &Input) -> Output {
|
||||
let mut cache = BTreeMap::new();
|
||||
max_flow(input, &mut cache, &mut input.to_open.clone(), "AA", 30)
|
||||
}
|
||||
|
||||
fn part_2(input: &Input) -> Output {
|
||||
let mut cache: BTreeMap<String, Output> = BTreeMap::new();
|
||||
|
||||
pairings(&input.to_open)
|
||||
.map(|(mut human, mut elephant)| {
|
||||
max_flow(input, &mut cache, &mut human, "AA", 26)
|
||||
+ max_flow(input, &mut cache, &mut elephant, "AA", 26)
|
||||
})
|
||||
.max()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Day16::run("sample")
|
||||
Day16::main()
|
||||
}
|
||||
|
||||
test_sample!(day_16, Day16, 1651, 1707);
|
Loading…
Reference in a new issue