|
14 | 14 | //! ```
|
15 | 15 | //!
|
16 | 16 | //! Now we can [BFS](https://en.wikipedia.org/wiki/Breadth-first_search) from any arbitrary
|
17 |
| -//! start time. Squares are blocked only if the grid time is less than or equal to the start time. |
18 |
| -//! |
19 |
| -//! A [binary search](https://en.wikipedia.org/wiki/Binary_search) is much faster than a |
20 |
| -//! linear search with complexity `O(log₂n)` vs `O(n)`. For example `log₂(3450) = 12`. |
| 17 | +//! start time. Squares are allowed if the grid time is greater than the start time. |
21 | 18 | use crate::util::grid::*;
|
22 | 19 | use crate::util::iter::*;
|
23 | 20 | use crate::util::parse::*;
|
24 | 21 | use crate::util::point::*;
|
| 22 | +use crate::util::heap::*; |
25 | 23 | use std::collections::VecDeque;
|
26 | 24 |
|
27 |
| -pub fn parse(input: &str) -> Grid<u16> { |
28 |
| - let mut grid = Grid::new(71, 71, u16::MAX); |
| 25 | +pub fn parse(input: &str) -> Grid<i32> { |
| 26 | + let mut grid = Grid::new(71, 71, i32::MAX); |
29 | 27 |
|
30 | 28 | for (i, [x, y]) in input.iter_signed::<i32>().chunk::<2>().enumerate() {
|
31 |
| - grid[Point::new(x, y)] = i as u16; |
| 29 | + grid[Point::new(x, y)] = i as i32; |
32 | 30 | }
|
33 | 31 |
|
34 | 32 | grid
|
35 | 33 | }
|
36 | 34 |
|
37 |
| -pub fn part1(grid: &Grid<u16>) -> u32 { |
38 |
| - bfs(grid, 1024).unwrap() |
39 |
| -} |
40 |
| - |
41 |
| -pub fn part2(grid: &Grid<u16>) -> String { |
42 |
| - let mut lower = 0; |
43 |
| - let mut upper = 5041; |
44 |
| - |
45 |
| - while lower < upper { |
46 |
| - let middle = (lower + upper) / 2; |
47 |
| - if bfs(grid, middle).is_some() { |
48 |
| - lower = middle + 1; |
49 |
| - } else { |
50 |
| - upper = middle; |
51 |
| - } |
52 |
| - } |
53 |
| - |
54 |
| - let index = grid.bytes.iter().position(|&time| time == lower).unwrap() as i32; |
55 |
| - format!("{},{}", index % grid.width, index / grid.width) |
56 |
| -} |
57 |
| - |
58 |
| -fn bfs(grid: &Grid<u16>, time: u16) -> Option<u32> { |
| 35 | +pub fn part1(grid: &Grid<i32>) -> u32 { |
| 36 | + let mut grid = grid.clone(); |
59 | 37 | let mut todo = VecDeque::new();
|
60 |
| - let mut seen = grid.clone(); |
61 | 38 |
|
| 39 | + grid[ORIGIN] = 0; |
62 | 40 | todo.push_back((ORIGIN, 0));
|
63 |
| - seen[ORIGIN] = 0; |
64 | 41 |
|
65 | 42 | while let Some((position, cost)) = todo.pop_front() {
|
66 | 43 | if position == Point::new(70, 70) {
|
67 |
| - return Some(cost); |
| 44 | + return cost; |
68 | 45 | }
|
69 | 46 |
|
70 | 47 | for next in ORTHOGONAL.map(|o| position + o) {
|
71 |
| - if seen.contains(next) && time < seen[next] { |
| 48 | + if grid.contains(next) && grid[next] > 1024 { |
| 49 | + grid[next] = 0; |
72 | 50 | todo.push_back((next, cost + 1));
|
73 |
| - seen[next] = 0; |
74 | 51 | }
|
75 | 52 | }
|
76 | 53 | }
|
77 | 54 |
|
78 |
| - None |
| 55 | + unreachable!() |
| 56 | +} |
| 57 | + |
| 58 | +pub fn part2(grid: &Grid<i32>) -> String { |
| 59 | + let mut time = i32::MAX; |
| 60 | + let mut heap = MinHeap::new(); |
| 61 | + |
| 62 | + let mut grid = grid.clone(); |
| 63 | + let mut todo = VecDeque::new(); |
| 64 | + |
| 65 | + grid[ORIGIN] = 0; |
| 66 | + todo.push_back(ORIGIN); |
| 67 | + |
| 68 | + loop { |
| 69 | + while let Some(position) = todo.pop_front() { |
| 70 | + if position == Point::new(70, 70) { |
| 71 | + let index = grid.bytes.iter().position(|&b| b == time).unwrap() as i32; |
| 72 | + return format!("{},{}", index % grid.width, index / grid.width); |
| 73 | + } |
| 74 | + |
| 75 | + for next in ORTHOGONAL.map(|o| position + o) { |
| 76 | + if grid.contains(next) { |
| 77 | + if time < grid[next] { |
| 78 | + grid[next] = 0; |
| 79 | + todo.push_back(next); |
| 80 | + } else { |
| 81 | + heap.push(-grid[next], next); |
| 82 | + } |
| 83 | + } |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + let (first, saved) = heap.pop().unwrap(); |
| 88 | + time = -first; |
| 89 | + todo.push_back(saved); |
| 90 | + } |
79 | 91 | }
|
0 commit comments