Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How Does Day 16 Work? #1

Open
jjorissen52 opened this issue Apr 6, 2023 · 0 comments
Open

How Does Day 16 Work? #1

jjorissen52 opened this issue Apr 6, 2023 · 0 comments

Comments

@jjorissen52
Copy link

jjorissen52 commented Apr 6, 2023

I've been struggling to complete day 16, my part 1 runs but part 2 is just too slow, and I came across this repo while looking for ways to optimize my solution (thanks for making this repository public, BTW)! I've been looking over your solutions trying to understand them. It's mostly clear and concise, very well written, but I don't quite understand how your solution to Day 16 works. Link to Day 16 in case you need a refresher.

I'll copy and paste your solution below with comments that I've added which explain my thoughts

use hashbrown::HashMap;
use itertools::Itertools;

#[aoc::main(16)]
fn main(input: &str) -> (u16, u16) {
  // vec of valve tuples in descending order of flow
  let valves = input.lines()
    .map(|l| {
      let mut words = l.split(|c: char| !c.is_uppercase() && !c.is_ascii_digit()).filter(|w| !w.is_empty()).skip(1);
      let valve = words.next().unwrap();
      let flow = words.next().unwrap().parse::<u16>().unwrap();
      let tunnels = words.collect::<Vec<_>>();
      (valve, flow, tunnels)
    })
    .sorted_by_key(|(_, flow, _)| -(*flow as i32))
    .collect::<Vec<_>>();
  // map of labels to their index in the valves, flow, and adj vectors
  let labels = valves.iter().enumerate().map(|(i, v)| (v.0, i)).collect::<HashMap<_, _>>();
  let flow = valves.iter().map(|(_,flow,_)| *flow).collect::<Vec<_>>();
  // vec of available valves from each valve
  let adj = valves.iter().map(|(_, _, tunnels)| tunnels.iter().map(|t| labels[t]).collect::<Vec<_>>()).collect::<Vec<_>>();
  // index marking ending of all "interesting" valves
  let m = valves.iter().position(|(_, flow, _)| *flow == 0).unwrap();
  // 2^(interesting_valves.len()), number of possible configurations of "interesting" valves ??
  let mm = 1 << m;

  // opt[time][node][available valves]
  let mut opt = vec![vec![vec![0; mm]; valves.len()]; 30];
  for t in 1..30 { // iterate over time...
    for i in 0..valves.len() { // iterate over all valves
      let ii = 1 << i; // 2^i
      for x in 0..mm { // iterate over all possible valve configurations
        let mut tmp = opt[t][i][x]; // should always be 0
        if ii & x != 0 && t >= 2 { // if this valve should be "on" for this particular configuration
          // I don't understand what x - ii is, or why it's flow[i] * t... isn't that backwards if we are progressing forwards in time?
          // how does this take into account the "cost" to get to a new location?
          tmp = tmp.max(opt[t-1][i][x - ii] + flow[i] * t as u16);
        }
        // optimal for this iteration is max(this location, adjacent locations...)
        opt[t][i][x] = tmp.max(adj[i].iter().map(|&j| opt[t-1][j][x]).max().unwrap());
      }
    }
  }

  let start = labels["AA"];
  let p1 = opt[29][start][mm - 1]; // opt[t of interest][starting position][??]
  // ??
  let p2 = (0..mm/2).map(|x| opt[25][start][x] + opt[25][start][mm-1-x]).max().unwrap();
  (p1, p2)
}

I would really appreciate it if you'd take the time to explain this to me. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant