|
1 | 1 | use common::{solution, Answer};
|
2 |
| -use itertools::Itertools; |
| 2 | +use itertools::{repeat_n, Itertools}; |
3 | 3 |
|
4 | 4 | solution!("Disk Fragmenter", 9);
|
5 | 5 |
|
6 | 6 | fn part_a(input: &str) -> Answer {
|
7 |
| - let mut problem = Problem::parse(input); |
8 |
| - problem.sort_blocks(); |
9 |
| - problem.score_blocks().into() |
| 7 | + let mut problem = Blocks::parse(input); |
| 8 | + problem.sort(); |
| 9 | + problem.score().into() |
10 | 10 | }
|
11 | 11 |
|
12 | 12 | fn part_b(input: &str) -> Answer {
|
13 |
| - let mut problem = Problem::parse(input); |
14 |
| - problem.sort_files(); |
15 |
| - problem.score_files().into() |
| 13 | + let mut problem = Files::parse(input); |
| 14 | + problem.sort(); |
| 15 | + problem.score().into() |
16 | 16 | }
|
17 | 17 |
|
18 |
| -#[derive(Debug)] |
19 |
| -struct Problem { |
| 18 | +struct Blocks { |
20 | 19 | blocks: Vec<Option<u32>>,
|
| 20 | +} |
| 21 | + |
| 22 | +struct Files { |
21 | 23 | files: Vec<File>,
|
22 | 24 | }
|
23 | 25 |
|
24 |
| -#[derive(Debug)] |
25 | 26 | struct File {
|
26 | 27 | pos: u32,
|
27 | 28 | size: u8,
|
28 | 29 | id: u32,
|
29 | 30 | }
|
30 | 31 |
|
31 |
| -impl Problem { |
| 32 | +impl Blocks { |
32 | 33 | fn parse(input: &str) -> Self {
|
33 | 34 | let mut blocks = Vec::new();
|
34 |
| - let mut files = Vec::new(); |
35 | 35 |
|
36 | 36 | let mut id = 0;
|
37 |
| - let mut pos = 0; |
38 |
| - |
39 | 37 | for (idx, chr) in input.trim().char_indices() {
|
40 |
| - let num = chr.to_digit(10).unwrap() as u8; |
| 38 | + let count = chr.to_digit(10).unwrap() as u8; |
41 | 39 |
|
42 |
| - if idx % 2 == 1 { |
43 |
| - for _ in 0..num { |
44 |
| - blocks.push(None); |
45 |
| - } |
46 |
| - } else { |
47 |
| - files.push(File { pos, size: num, id }); |
48 |
| - for _ in 0..num { |
49 |
| - blocks.push(Some(id)); |
50 |
| - } |
51 |
| - id += 1; |
52 |
| - } |
| 40 | + let is_block = idx % 2 == 0; |
| 41 | + let item = is_block.then_some(id); |
53 | 42 |
|
54 |
| - pos += num as u32; |
| 43 | + blocks.extend(repeat_n(item, count as usize)); |
| 44 | + id += is_block as u32; |
55 | 45 | }
|
56 | 46 |
|
57 |
| - Self { blocks, files } |
| 47 | + Self { blocks } |
58 | 48 | }
|
59 | 49 |
|
60 |
| - fn sort_blocks(&mut self) { |
| 50 | + fn sort(&mut self) { |
61 | 51 | loop {
|
62 | 52 | let empty = self.blocks.iter().position(|x| x.is_none()).unwrap();
|
63 | 53 | let last = self.blocks.iter().rposition(|x| x.is_some()).unwrap();
|
64 | 54 |
|
65 |
| - if last > empty { |
66 |
| - self.blocks.swap(empty, last); |
67 |
| - } else { |
| 55 | + if last <= empty { |
68 | 56 | break;
|
69 | 57 | }
|
| 58 | + |
| 59 | + self.blocks.swap(empty, last); |
70 | 60 | }
|
71 | 61 | }
|
72 | 62 |
|
73 |
| - fn sort_files(&mut self) { |
| 63 | + fn score(&self) -> u64 { |
| 64 | + self.blocks |
| 65 | + .iter() |
| 66 | + .enumerate() |
| 67 | + .map(|(idx, id)| idx as u64 * id.unwrap_or_default() as u64) |
| 68 | + .sum() |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +impl Files { |
| 73 | + fn parse(input: &str) -> Self { |
| 74 | + let mut files = Vec::new(); |
| 75 | + let (mut id, mut pos) = (0, 0); |
| 76 | + |
| 77 | + for (idx, chr) in input.trim().char_indices() { |
| 78 | + let size = chr.to_digit(10).unwrap() as u8; |
| 79 | + |
| 80 | + if idx % 2 == 0 { |
| 81 | + files.push(File { pos, size, id }); |
| 82 | + id += 1; |
| 83 | + } |
| 84 | + |
| 85 | + pos += size as u32; |
| 86 | + } |
| 87 | + |
| 88 | + Self { files } |
| 89 | + } |
| 90 | + |
| 91 | + fn sort(&mut self) { |
74 | 92 | let max_id = self.files.last().unwrap().id;
|
75 | 93 | for id in (0..=max_id).rev() {
|
76 | 94 | let file_idx = self.files.iter().position(|x| x.id == id).unwrap();
|
@@ -99,32 +117,20 @@ impl Problem {
|
99 | 117 | }
|
100 | 118 | }
|
101 | 119 |
|
102 |
| - fn score_blocks(&self) -> u64 { |
103 |
| - let mut sum = 0; |
104 |
| - |
105 |
| - for (idx, id) in self.blocks.iter().enumerate() { |
106 |
| - sum += idx as u64 * id.unwrap_or_default() as u64; |
107 |
| - } |
108 |
| - |
109 |
| - sum |
110 |
| - } |
111 |
| - |
112 |
| - fn score_files(&self) -> u64 { |
| 120 | + fn score(&self) -> u64 { |
113 | 121 | let mut sum = 0;
|
114 | 122 | let mut last = 0;
|
115 | 123 | let mut idx = 0;
|
| 124 | + |
116 | 125 | for x in &self.files {
|
117 |
| - for _ in last..x.pos { |
118 |
| - idx += 1; |
119 |
| - } |
| 126 | + idx += x.pos - last; |
120 | 127 |
|
121 |
| - for _ in 0..x.size { |
122 |
| - sum += idx * x.id as u64; |
123 |
| - idx += 1; |
124 |
| - } |
| 128 | + sum += (x.id as u64 * x.size as u64 * (x.size as u64 + 2 * idx as u64 - 1)) / 2; |
| 129 | + idx += x.size as u32; |
125 | 130 |
|
126 | 131 | last = x.pos + x.size as u32;
|
127 | 132 | }
|
| 133 | + |
128 | 134 | sum
|
129 | 135 | }
|
130 | 136 | }
|
|
0 commit comments