/ Rust / 2024 / 09.rs
09.rs
 1  #![feature(test)]
 2  
 3  use std::{
 4      cmp::Reverse,
 5      collections::{BinaryHeap, VecDeque},
 6  };
 7  
 8  type Input = Vec<u8>;
 9  
10  fn setup(input: &str) -> Input {
11      input.trim().bytes().map(|b| b - b'0').collect()
12  }
13  
14  fn part1(input: &Input) -> u64 {
15      let mut files = Vec::new();
16      let mut free = VecDeque::new();
17      let mut pos = 0;
18      for (i, len) in input.iter().map(|&x| x as u64).enumerate() {
19          if i & 1 == 0 {
20              files.push((pos, len, i as u64 >> 1));
21          } else if len > 0 {
22              free.push_back((pos, len));
23          }
24          pos += len;
25      }
26  
27      files
28          .iter()
29          .flat_map(|&(file_pos, file_len, file_id)| {
30              (file_pos..file_pos + file_len).map(move |pos| (pos, file_id))
31          })
32          .rev()
33          .map(|(mut pos, file_id)| {
34              if let Some((free_pos, free_len)) = free.front_mut().filter(|free| free.0 < pos) {
35                  pos = *free_pos;
36                  *free_pos += 1;
37                  *free_len -= 1;
38                  if *free_len == 0 {
39                      free.pop_front();
40                  }
41              }
42              pos * file_id
43          })
44          .sum()
45  }
46  
47  fn part2(input: &Input) -> u64 {
48      let mut files = Vec::new();
49      let mut free_by_len = [const { BinaryHeap::new() }; 10];
50      let mut pos = 0;
51      for (i, len) in input.iter().map(|&x| x as u64).enumerate() {
52          if i & 1 == 0 {
53              files.push((pos, len, i as u64 >> 1));
54          } else if len > 0 {
55              free_by_len[len as usize].push(Reverse(pos));
56          }
57          pos += len;
58      }
59  
60      files
61          .iter()
62          .rev()
63          .map(|&(file_pos, file_len, file_id)| {
64              let pos = (file_len as _..free_by_len.len())
65                  .filter_map(|len| free_by_len[len].peek().map(|&free_pos| (free_pos.0, len)))
66                  .min()
67                  .filter(|&(free_pos, _)| free_pos < file_pos)
68                  .map(|(free_pos, free_len)| {
69                      free_by_len[free_len].pop();
70                      if free_len > file_len as usize {
71                          free_by_len[free_len - file_len as usize]
72                              .push(Reverse(free_pos + file_len));
73                      }
74                      free_pos
75                  })
76                  .unwrap_or(file_pos);
77              file_len * (pos * 2 + file_len - 1) / 2 * file_id
78          })
79          .sum()
80  }
81  
82  aoc::main!(2024, 9, ex: 1);