diff options
| author | Sebastiano Tronto <sebastiano@tronto.net> | 2025-07-03 11:10:12 +0200 |
|---|---|---|
| committer | Sebastiano Tronto <sebastiano@tronto.net> | 2025-07-03 11:10:12 +0200 |
| commit | 36c94296df6edce01db71afeadb8940e33ecdd4a (patch) | |
| tree | 72a48963ad79e99819339ac865e8a4f89e8d68aa /2022/16/a.rs | |
| parent | da1e366c3d6fd6b746a7eb34cf7124adcbd15805 (diff) | |
| download | aoc-36c94296df6edce01db71afeadb8940e33ecdd4a.tar.gz aoc-36c94296df6edce01db71afeadb8940e33ecdd4a.zip | |
Day 16 2022, but part b is slow
Diffstat (limited to '2022/16/a.rs')
| -rw-r--r-- | 2022/16/a.rs | 112 |
1 files changed, 71 insertions, 41 deletions
diff --git a/2022/16/a.rs b/2022/16/a.rs index d6f3569..da9a881 100644 --- a/2022/16/a.rs +++ b/2022/16/a.rs | |||
| @@ -10,14 +10,16 @@ struct Node { | |||
| 10 | 10 | ||
| 11 | #[derive(Debug, Copy, Clone)] | 11 | #[derive(Debug, Copy, Clone)] |
| 12 | struct MapState { | 12 | struct MapState { |
| 13 | max_pos: usize, | ||
| 13 | valves: usize, | 14 | valves: usize, |
| 14 | pos_ind: usize, | 15 | pub pos: usize |
| 15 | max_pos: usize | ||
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | #[derive(Debug, Clone)] | 18 | #[derive(Debug, Clone)] |
| 19 | struct Map { | 19 | struct Map { |
| 20 | index: HashMap<u16, usize>, | 20 | index: HashMap<u16, usize>, |
| 21 | state_index: HashMap<u16, usize>, | ||
| 22 | pressure: Vec<usize>, | ||
| 21 | nodes: Vec<Node> | 23 | nodes: Vec<Node> |
| 22 | } | 24 | } |
| 23 | 25 | ||
| @@ -44,9 +46,34 @@ impl Node { | |||
| 44 | } | 46 | } |
| 45 | } | 47 | } |
| 46 | 48 | ||
| 49 | impl MapState { | ||
| 50 | fn from_usize(i: usize, map: &Map) -> MapState { | ||
| 51 | let max_pos = map.index.len(); | ||
| 52 | MapState { | ||
| 53 | max_pos, | ||
| 54 | valves: i / max_pos, | ||
| 55 | pos: i % max_pos | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | fn to_usize(&self) -> usize { | ||
| 60 | self.max_pos * self.valves + self.pos | ||
| 61 | } | ||
| 62 | |||
| 63 | fn get_valve(&self, i: usize) -> bool { | ||
| 64 | self.valves & (1 << i) != 0 | ||
| 65 | } | ||
| 66 | |||
| 67 | fn toggle_valve(&mut self, i: usize) { | ||
| 68 | self.valves ^= 1 << i; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 47 | impl Map { | 72 | impl Map { |
| 48 | fn from_stdin() -> Map { | 73 | fn from_stdin() -> Map { |
| 49 | let mut line = String::new(); | 74 | let mut line = String::new(); |
| 75 | |||
| 76 | // Get the nodes | ||
| 50 | let mut nodes = Vec::<Node>::new(); | 77 | let mut nodes = Vec::<Node>::new(); |
| 51 | while std::io::stdin().read_line(&mut line).unwrap() > 0 { | 78 | while std::io::stdin().read_line(&mut line).unwrap() > 0 { |
| 52 | let node = Node::from_line(&line); | 79 | let node = Node::from_line(&line); |
| @@ -58,78 +85,81 @@ impl Map { | |||
| 58 | } | 85 | } |
| 59 | line.clear(); | 86 | line.clear(); |
| 60 | } | 87 | } |
| 61 | let index = nodes.iter() | ||
| 62 | .enumerate() | ||
| 63 | .map(|(i, n)| (n.name, i)) | ||
| 64 | .collect::<HashMap<_, _>>(); | ||
| 65 | Map { index, nodes } | ||
| 66 | } | ||
| 67 | 88 | ||
| 68 | fn state_max(&self) -> usize { | 89 | // Compute the node-name-to-index tables |
| 69 | let mut i: usize = 0; | 90 | let mut j = 0; |
| 70 | let mut tot: usize = 0; | 91 | let mut index = HashMap::<u16, usize>::new(); |
| 71 | for n in &self.nodes { | 92 | let mut state_index = HashMap::<u16, usize>::new(); |
| 72 | if n.rate == 0 { continue; } | 93 | let mut pressure = vec![0]; |
| 73 | tot += 1 << i; | 94 | for i in 0..nodes.len() { |
| 74 | i += 1; | 95 | index.insert(nodes[i].name, i); |
| 96 | if nodes[i].rate != 0 { | ||
| 97 | state_index.insert(nodes[i].name, j); | ||
| 98 | j += 1; | ||
| 99 | |||
| 100 | // Pre-compute flow rate for all states with node[i] turned on | ||
| 101 | for k in 0..pressure.len() { | ||
| 102 | pressure.push(pressure[k] + nodes[i].rate); | ||
| 103 | } | ||
| 104 | } | ||
| 75 | } | 105 | } |
| 76 | let len = self.nodes.len(); | 106 | |
| 77 | tot * len + len | 107 | Map { index, state_index, pressure, nodes } |
| 78 | } | 108 | } |
| 79 | 109 | ||
| 80 | fn released_pressure(&self) -> usize { | 110 | fn state_max(&self) -> usize { |
| 81 | self.nodes.iter() | 111 | self.index.len() * (1 << self.state_index.len()) |
| 82 | .map(|n| if n.valve_on { n.rate } else { 0 }) | ||
| 83 | .sum() | ||
| 84 | } | 112 | } |
| 85 | 113 | ||
| 86 | fn most_pressure(&self, minutes: usize) -> usize { | 114 | fn most_pressure(&self, minutes: usize) -> usize { |
| 87 | let n = self.state_max(); | 115 | let n = self.state_max(); |
| 88 | let mut t = vec![0 as usize; n]; | 116 | let mut t = vec![0 as usize; n]; |
| 89 | let mut u = vec![0 as usize; n]; | 117 | let mut u = vec![0 as usize; n]; |
| 90 | let mut map = self.clone(); | ||
| 91 | 118 | ||
| 92 | // Setup table for last minute | 119 | // Setup table for last minute |
| 93 | for s in 0..n { | 120 | for s in 0..n { |
| 94 | map.set_state(s); | 121 | t[s] = self.pressure[MapState::from_usize(s, self).valves]; |
| 95 | t[s] = map.released_pressure(); | 122 | u[s] = t[s]; |
| 96 | } | 123 | } |
| 97 | 124 | ||
| 98 | // Dynamic programming counting back to 0 minutes | 125 | // Dynamic programming counting back to 0 minutes |
| 99 | for i in (0..minutes).rev() { | 126 | for i in 0..minutes-1 { |
| 100 | // Double buffer technique so we don't have to swap vectors | 127 | // Double buffer technique so we don't have to swap vectors |
| 101 | let (current, next) = if i % 2 == minutes % 2 { | 128 | let (current, next) = if i % 2 == 0 { |
| 102 | (&mut t, &mut u) | 129 | (&mut t, &u) |
| 103 | } else { | 130 | } else { |
| 104 | (&mut u, &mut t) | 131 | (&mut u, &t) |
| 105 | }; | 132 | }; |
| 106 | for s in 0..n { | 133 | for s in 0..n { |
| 107 | map.set_state(s); | 134 | let mut state = MapState::from_usize(s, self); |
| 108 | let pos = map.pos; | ||
| 109 | let mut k = 0; | 135 | let mut k = 0; |
| 110 | let ind = map.index[&pos]; | 136 | let ind = self.index[&self.nodes[state.pos].name]; |
| 111 | let p = map.released_pressure(); | 137 | let p = self.pressure[state.valves]; |
| 112 | 138 | ||
| 113 | // Try turning on the valve | 139 | // Try turning on the valve |
| 114 | if map.nodes[ind].rate > 0 && !map.nodes[ind].valve_on { | 140 | if self.nodes[ind].rate > 0 { |
| 115 | map.nodes[ind].valve_on = true; | 141 | let si = self.state_index[&self.nodes[ind].name]; |
| 116 | k = max(k, p + next[map.get_state()]); | 142 | if !state.get_valve(si) { |
| 117 | map.nodes[ind].valve_on = false; | 143 | state.toggle_valve(si); |
| 144 | k = max(k, p + next[state.to_usize()]); | ||
| 145 | state.toggle_valve(si); | ||
| 146 | } | ||
| 118 | } | 147 | } |
| 119 | 148 | ||
| 120 | // Try moving to a neighbor | 149 | // Try moving to a neighbor |
| 121 | for v in &map.nodes[ind].neighbors { | 150 | for v in &self.nodes[ind].neighbors { |
| 122 | map.pos = *v; | 151 | state.pos = self.index[v]; |
| 123 | k = max(k, p + next[map.get_state()]); | 152 | k = max(k, p + next[state.to_usize()]); |
| 124 | } | 153 | } |
| 125 | current[s] = k; | 154 | current[s] = k; |
| 126 | } | 155 | } |
| 127 | } | 156 | } |
| 128 | if 0 == minutes % 2 { t[0] } else { u[0] } | 157 | if 0 == minutes % 2 { t[0] } else { u[0] } // Initial state = 0 |
| 129 | } | 158 | } |
| 130 | } | 159 | } |
| 131 | 160 | ||
| 132 | fn main() { | 161 | fn main() { |
| 133 | let map = Map::from_stdin(); | 162 | let map = Map::from_stdin(); |
| 134 | println!("{}", map.most_pressure(29)); | 163 | let result = map.most_pressure(30); |
| 164 | println!("{result}"); | ||
| 135 | } | 165 | } |
