1- use crate :: util:: grid:: * ;
2- use crate :: util:: hash:: * ;
31use crate :: util:: iter:: * ;
42use 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 ;
113
124type Tile = [ u64 ; 2 ] ;
135
@@ -30,75 +22,140 @@ pub fn part1(tiles: &[Tile]) -> u64 {
3022}
3123
3224pub fn part2 ( tiles : & [ Tile ] ) -> u64 {
33- let size = tiles. len ( ) ;
34- let shrink_x = shrink ( tiles, 0 ) ;
35- let shrink_y = shrink ( tiles, 1 ) ;
36- let shrunk: Vec < _ > = tiles. iter ( ) . map ( |& [ x, y] | ( shrink_x[ & x] , shrink_y[ & y] ) ) . collect ( ) ;
25+ let mut tiles = tiles. to_vec ( ) ;
3726
38- let mut area = 0 ;
39- let mut todo = VecDeque :: from ( [ ORIGIN ] ) ;
40- let mut grid = Grid :: new ( shrink_x. len ( ) as i32 , shrink_y. len ( ) as i32 , UNKNOWN ) ;
27+ tiles. sort_unstable_by_key ( |& [ x, y] | ( y, x) ) ;
4128
42- for i in 0 ..size {
43- let ( x1, y1, x2, y2) = minmax ( shrunk[ i] , shrunk[ ( i + 1 ) % size] ) ;
29+ let tiles = tiles;
4430
45- for x in x1..x2 + 1 {
46- for y in y1..y2 + 1 {
47- grid[ Point :: new ( x, y) ] = INSIDE ;
48- }
49- }
31+ // Track the largest area so far during scanning:
32+ let mut largest_area: u64 = 0 ;
33+
34+ // Each red tile (`x`, `y`) becomes a candidate for being a top corner of the largest area, and during the
35+ // scan the `interval` containing the maximum possible width is updated:
36+ struct Candidate {
37+ x : u64 ,
38+ y : u64 ,
39+ interval : Interval ,
5040 }
5141
52- while let Some ( point) = todo. pop_front ( ) {
53- for next in ORTHOGONAL . map ( |o| point + o) {
54- if grid. contains ( next) && grid[ next] == UNKNOWN {
55- grid[ next] = OUTSIDE ;
56- todo. push_back ( next) ;
42+ let mut candidates: Vec < Candidate > = Vec :: with_capacity ( 512 ) ;
43+
44+ // Maintain an ordered list of descending edges, i.e. [begin_interval_0, end_interval_0, begin_interval_1, end_interval_1, ...]:
45+ let mut descending_edges: Vec < u64 > = vec ! [ ] ;
46+ let mut intervals_from_descending_edges = vec ! [ ] ;
47+
48+ // Invariants on the input data (defined by the puzzle) result in points arriving in pairs on the same y line:
49+ let mut it = tiles. into_iter ( ) ;
50+
51+ while let ( Some ( [ x0, y] ) , Some ( [ x1, y1] ) ) = ( it. next ( ) , it. next ( ) ) {
52+ debug_assert_eq ! ( y, y1) ;
53+
54+ // Update the descending edges; since we are scanning from top to bottom, and within each line left to right,
55+ // when we, starting from outside of the region, hit a corner tile it is either:
56+ //
57+ // - The corner of two edges, one going right and one going down. In this case, the `descending_edges` won't contain
58+ // the `x` coordinate, and we should "toggle" it on to denote that there is a new descending edge.
59+ // - The corner of two edges, one going right and one going up. The `descending_edges` will contain an `x` coordinate,
60+ // that should be "toggled" off.
61+ //
62+ // Simular arguments work for when we are scanning inside the edge and we hit the corner that ends the edge; this is also
63+ // why corners always arrive in pairs.
64+ //
65+ // Do the update:
66+ for x in [ x0, x1] {
67+ toggle_value_membership_in_ordered_list ( & mut descending_edges, x) ;
68+ }
69+
70+ // Every pair of descending edges in the ordered list defines a region; find the resulting intervals on this line:
71+ update_intervals_from_descending_edges ( & descending_edges, & mut intervals_from_descending_edges) ;
72+
73+ // Check the rectangles this red tile could be a bottom tile for, with the current candidates:
74+ for candidate in candidates. iter ( ) {
75+ for x in [ x0, x1] {
76+ if candidate. interval . contains ( x) {
77+ largest_area = largest_area. max ( ( candidate. x . abs_diff ( x) + 1 ) * ( candidate. y . abs_diff ( y) + 1 ) ) ;
78+ }
79+ }
80+ }
81+
82+ // Update candidates when their interval shrinks due to descending edge changes, and drop them when their interval becomes empty:
83+ candidates. retain_mut ( |candidate| {
84+ if let Some ( intersection_containing_x) = intervals_from_descending_edges. iter ( ) . find ( |i| i. contains ( candidate. x ) ) {
85+ candidate. interval = intersection_containing_x. intersection ( candidate. interval ) ;
86+
87+ true
88+ } else {
89+ false
90+ }
91+ } ) ;
92+
93+ // Add any new candidates:
94+ for x in [ x0, x1] {
95+ if let Some ( & containing) = intervals_from_descending_edges. iter ( ) . find ( |i| i. contains ( x) ) {
96+ candidates. push ( Candidate { x, y, interval : containing } ) ;
5797 }
5898 }
5999 }
60100
61- for y in 1 ..grid. height {
62- for x in 1 ..grid. width {
63- let point = Point :: new ( x, y) ;
64- let value = i64:: from ( grid[ point] != OUTSIDE ) ;
65- grid[ point] = value + grid[ point + UP ] + grid[ point + LEFT ] - grid[ point + UP + LEFT ] ;
66- }
101+ largest_area
102+ }
103+
104+ /// The set { x in u64 | l <= x <= r }.
105+ #[ derive( Clone , Copy ) ]
106+ struct Interval {
107+ l : u64 ,
108+ r : u64 ,
109+ }
110+
111+ impl Interval {
112+ fn new ( l : u64 , r : u64 ) -> Self {
113+ debug_assert ! ( l <= r) ;
114+
115+ Interval { l, r }
67116 }
68117
69- for i in 0 ..size {
70- for j in i + 1 ..size {
71- let ( x1, y1, x2, y2) = minmax ( shrunk[ i] , shrunk[ j] ) ;
72-
73- let expected = ( x2 - x1 + 1 ) as i64 * ( y2 - y1 + 1 ) as i64 ;
74- let actual = grid[ Point :: new ( x2, y2) ]
75- - grid[ Point :: new ( x1 - 1 , y2) ]
76- - grid[ Point :: new ( x2, y1 - 1 ) ]
77- + grid[ Point :: new ( x1 - 1 , y1 - 1 ) ] ;
78-
79- if expected == actual {
80- let [ x1, y1] = tiles[ i] ;
81- let [ x2, y2] = tiles[ j] ;
82- let dx = x1. abs_diff ( x2) + 1 ;
83- let dy = y1. abs_diff ( y2) + 1 ;
84- area = area. max ( dx * dy) ;
85- }
86- }
118+ fn intersects ( self , other : Self ) -> bool {
119+ other. l <= self . r && self . l <= other. r
87120 }
88121
89- area
122+ fn intersection ( self , other : Self ) -> Self {
123+ debug_assert ! ( self . intersects( other) ) ;
124+
125+ Interval :: new ( self . l . max ( other. l ) , self . r . min ( other. r ) )
126+ }
127+
128+ fn contains ( self , x : u64 ) -> bool {
129+ self . l <= x && x <= self . r
130+ }
90131}
91132
92- fn shrink ( tiles : & [ Tile ] , index : usize ) -> FastMap < u64 , i32 > {
93- let mut axis: Vec < _ > = tiles. iter ( ) . map ( |tile| tile[ index] ) . collect ( ) ;
94- axis. push ( u64:: MIN ) ;
95- axis. push ( u64:: MAX ) ;
96- axis. sort_unstable ( ) ;
97- axis. dedup ( ) ;
98- axis. iter ( ) . enumerate ( ) . map ( |( i, & n) | ( n, i as i32 ) ) . collect ( )
133+ // Adds `value` if it isn't in `ordered_list`, removes it if it is, maintaining the order.
134+ fn toggle_value_membership_in_ordered_list ( ordered_list : & mut Vec < u64 > , value : u64 ) {
135+ let mut i = 0 ;
136+
137+ while i < ordered_list. len ( ) && ordered_list[ i] < value {
138+ i += 1 ;
139+ }
140+
141+ if i == ordered_list. len ( ) || ordered_list[ i] != value {
142+ ordered_list. insert ( i, value) ;
143+ } else {
144+ ordered_list. remove ( i) ;
145+ }
99146}
100147
148+ // Changes the list of descending edges, [begin_interval_0, end_interval_0, begin_interval_1, end_interval_1, ...],
149+ // into a vector containing the intervals.
101150#[ inline]
102- fn minmax ( ( x1, y1) : ( i32 , i32 ) , ( x2, y2) : ( i32 , i32 ) ) -> ( i32 , i32 , i32 , i32 ) {
103- ( x1. min ( x2) , y1. min ( y2) , x1. max ( x2) , y1. max ( y2) )
151+ fn update_intervals_from_descending_edges ( descending_edges : & [ u64 ] , to_update : & mut Vec < Interval > ) {
152+ debug_assert ! ( descending_edges. len( ) . is_multiple_of( 2 ) ) ;
153+
154+ to_update. clear ( ) ;
155+
156+ let mut it = descending_edges. iter ( ) ;
157+
158+ while let ( Some ( & l) , Some ( & r) ) = ( it. next ( ) , it. next ( ) ) {
159+ to_update. push ( Interval :: new ( l, r) ) ;
160+ }
104161}
0 commit comments