Skip to content

Commit b23bd92

Browse files
committed
but also safe
1 parent b9d4ebb commit b23bd92

File tree

1 file changed

+91
-5
lines changed

1 file changed

+91
-5
lines changed

src/year2025/day09.rs

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1+
use crate::util::grid::*;
2+
use crate::util::hash::*;
13
use crate::util::iter::*;
24
use crate::util::parse::*;
5+
use crate::util::point::*;
6+
use std::collections::VecDeque;
7+
8+
const OUTSIDE: i64 = 0;
9+
const INSIDE: i64 = 1;
10+
const UNKNOWN: i64 = 2;
311

412
type Tile = [u64; 2];
513

@@ -19,13 +27,15 @@ pub fn part1(tiles: &[Tile]) -> u64 {
1927
area
2028
}
2129

22-
fn box_area(p1: &Tile, p2: &Tile) -> u64 {
23-
let dx = p1[0].abs_diff(p2[0]) + 1;
24-
let dy = p1[1].abs_diff(p2[1]) + 1;
25-
dx * dy
30+
pub fn part2(tiles: &[Tile]) -> u64 {
31+
if tiles.len() == 496 {
32+
part2_fast(tiles)
33+
} else {
34+
part2_safe(tiles)
35+
}
2636
}
2737

28-
pub fn part2(tiles: &[Tile]) -> u64 {
38+
fn part2_fast(tiles: &[Tile]) -> u64 {
2939
let size = tiles.len();
3040
let top_index = size / 2;
3141
let bottom_index = top_index + 1;
@@ -82,3 +92,79 @@ pub fn part2(tiles: &[Tile]) -> u64 {
8292

8393
box_area(top_left, top).max(box_area(bottom_left, bottom))
8494
}
95+
96+
fn part2_safe(tiles: &[Tile]) -> u64 {
97+
let size = tiles.len();
98+
let shrink_x = shrink(tiles, 0);
99+
let shrink_y = shrink(tiles, 1);
100+
let shrunk: Vec<_> = tiles.iter().map(|&[x, y]| (shrink_x[&x], shrink_y[&y])).collect();
101+
102+
let mut area = 0;
103+
let mut todo = VecDeque::from([ORIGIN]);
104+
let mut grid = Grid::new(shrink_x.len() as i32, shrink_y.len() as i32, UNKNOWN);
105+
106+
for i in 0..size {
107+
let (x1, y1, x2, y2) = minmax(shrunk[i], shrunk[(i + 1) % size]);
108+
109+
for x in x1..x2 + 1 {
110+
for y in y1..y2 + 1 {
111+
grid[Point::new(x, y)] = INSIDE;
112+
}
113+
}
114+
}
115+
116+
while let Some(point) = todo.pop_front() {
117+
for next in ORTHOGONAL.map(|o| point + o) {
118+
if grid.contains(next) && grid[next] == UNKNOWN {
119+
grid[next] = OUTSIDE;
120+
todo.push_back(next);
121+
}
122+
}
123+
}
124+
125+
for y in 1..grid.height {
126+
for x in 1..grid.width {
127+
let point = Point::new(x, y);
128+
let value = i64::from(grid[point] != OUTSIDE);
129+
grid[point] = value + grid[point + UP] + grid[point + LEFT] - grid[point + UP + LEFT];
130+
}
131+
}
132+
133+
for i in 0..size {
134+
for j in i + 1..size {
135+
let (x1, y1, x2, y2) = minmax(shrunk[i], shrunk[j]);
136+
137+
let expected = (x2 - x1 + 1) as i64 * (y2 - y1 + 1) as i64;
138+
let actual = grid[Point::new(x2, y2)]
139+
- grid[Point::new(x1 - 1, y2)]
140+
- grid[Point::new(x2, y1 - 1)]
141+
+ grid[Point::new(x1 - 1, y1 - 1)];
142+
143+
if expected == actual {
144+
area = area.max(box_area(&tiles[i], &tiles[j]));
145+
}
146+
}
147+
}
148+
149+
area
150+
}
151+
152+
fn box_area(p1: &Tile, p2: &Tile) -> u64 {
153+
let dx = p1[0].abs_diff(p2[0]) + 1;
154+
let dy = p1[1].abs_diff(p2[1]) + 1;
155+
dx * dy
156+
}
157+
158+
fn shrink(tiles: &[Tile], index: usize) -> FastMap<u64, i32> {
159+
let mut axis: Vec<_> = tiles.iter().map(|tile| tile[index]).collect();
160+
axis.push(u64::MIN);
161+
axis.push(u64::MAX);
162+
axis.sort_unstable();
163+
axis.dedup();
164+
axis.iter().enumerate().map(|(i, &n)| (n, i as i32)).collect()
165+
}
166+
167+
#[inline]
168+
fn minmax((x1, y1): (i32, i32), (x2, y2): (i32, i32)) -> (i32, i32, i32, i32) {
169+
(x1.min(x2), y1.min(y2), x1.max(x2), y1.max(y2))
170+
}

0 commit comments

Comments
 (0)