@@ -2,8 +2,13 @@ use crate::util::grid::*;
2
2
use crate :: util:: hash:: * ;
3
3
use crate :: util:: point:: * ;
4
4
use std:: collections:: VecDeque ;
5
+ use std:: sync:: atomic:: { AtomicU32 , Ordering } ;
6
+ use std:: thread;
5
7
6
8
pub struct Input {
9
+ start : usize ,
10
+ end : usize ,
11
+ extra : u32 ,
7
12
directed : [ u64 ; 36 ] ,
8
13
undirected : [ u64 ; 36 ] ,
9
14
weight : [ [ u32 ; 36 ] ; 36 ] ,
@@ -47,8 +52,8 @@ pub fn parse(input: &str) -> Input {
47
52
48
53
// BFS to find distances between POIs.
49
54
let mut todo = VecDeque :: new ( ) ;
50
- let mut directed = [ 0 ; 36 ] ;
51
- let mut undirected = [ 0 ; 36 ] ;
55
+ let mut directed: [ u64 ; 36 ] = [ 0 ; 36 ] ;
56
+ let mut undirected: [ u64 ; 36 ] = [ 0 ; 36 ] ;
52
57
let mut weight = [ [ 0 ; 36 ] ; 36 ] ;
53
58
54
59
for ( & start, & from) in & poi {
@@ -90,14 +95,34 @@ pub fn parse(input: &str) -> Input {
90
95
}
91
96
}
92
97
93
- Input { directed, undirected, weight }
98
+ // Compress
99
+ let start = undirected[ 0 ] . trailing_zeros ( ) as usize ;
100
+ let end = undirected[ 1 ] . trailing_zeros ( ) as usize ;
101
+ let extra = 2 + weight[ 0 ] [ start] + weight[ 1 ] [ end] ;
102
+
103
+ // Heuristic
104
+ let mut mask = 0 ;
105
+
106
+ for ( i, edges) in undirected. iter ( ) . enumerate ( ) {
107
+ if edges. count_ones ( ) < 4 {
108
+ mask |= 1 << i;
109
+ }
110
+ }
111
+
112
+ for ( i, edges) in undirected. iter_mut ( ) . enumerate ( ) {
113
+ if edges. count_ones ( ) < 4 {
114
+ * edges = ( * edges & !mask) | directed[ i] ;
115
+ }
116
+ }
117
+
118
+ Input { start, end, extra, directed, undirected, weight }
94
119
}
95
120
96
121
pub fn part1 ( input : & Input ) -> u32 {
97
122
let mut cost = [ 0 ; 36 ] ;
98
123
99
124
let mut todo = VecDeque :: new ( ) ;
100
- todo. push_back ( 0 ) ;
125
+ todo. push_back ( input . start ) ;
101
126
102
127
while let Some ( from) = todo. pop_front ( ) {
103
128
let mut nodes = input. directed [ from] ;
@@ -112,16 +137,57 @@ pub fn part1(input: &Input) -> u32 {
112
137
}
113
138
}
114
139
115
- 2 + cost[ 1 ]
140
+ cost[ input . end ] + input . extra
116
141
}
117
142
118
143
pub fn part2 ( input : & Input ) -> u32 {
119
- 2 + dfs ( input, 0 , 1 , 0 )
144
+ let shared = AtomicU32 :: new ( 0 ) ;
145
+ let threads = thread:: available_parallelism ( ) . unwrap ( ) . get ( ) ;
146
+
147
+ // Seed each worker thread with a starting state
148
+ let mut seeds = VecDeque :: new ( ) ;
149
+ seeds. push_back ( ( input. start , 1 << input. start , 0 ) ) ;
150
+
151
+ while seeds. len ( ) < threads {
152
+ let Some ( ( from, seen, cost) ) = seeds. pop_front ( ) else {
153
+ break ;
154
+ } ;
155
+
156
+ if from == input. end {
157
+ shared. fetch_max ( cost, Ordering :: Relaxed ) ;
158
+ continue ;
159
+ }
160
+
161
+ let mut nodes = input. undirected [ from] & !seen;
162
+
163
+ while nodes > 0 {
164
+ let to = nodes. trailing_zeros ( ) as usize ;
165
+ let mask = 1 << to;
166
+ nodes ^= mask;
167
+
168
+ seeds. push_back ( ( to, seen | mask, cost + input. weight [ from] [ to] ) ) ;
169
+ }
170
+ }
171
+
172
+ // Use as many cores as possible to parallelize the remaining search.
173
+ thread:: scope ( |scope| {
174
+ for start in & seeds {
175
+ scope. spawn ( || worker ( input, & shared, start) ) ;
176
+ }
177
+ } ) ;
178
+
179
+ shared. load ( Ordering :: Relaxed ) + input. extra
180
+ }
181
+
182
+ fn worker ( input : & Input , shared : & AtomicU32 , start : & ( usize , u64 , u32 ) ) {
183
+ let ( from, seen, cost) = * start;
184
+ let result = dfs ( input, from, seen) ;
185
+ shared. fetch_max ( result + cost, Ordering :: Relaxed ) ;
120
186
}
121
187
122
- fn dfs ( input : & Input , from : usize , seen : u64 , cost : u32 ) -> u32 {
123
- if from == 1 {
124
- return cost ;
188
+ fn dfs ( input : & Input , from : usize , seen : u64 ) -> u32 {
189
+ if from == input . end {
190
+ return 0 ;
125
191
}
126
192
127
193
let mut nodes = input. undirected [ from] & !seen;
@@ -132,7 +198,7 @@ fn dfs(input: &Input, from: usize, seen: u64, cost: u32) -> u32 {
132
198
let mask = 1 << to;
133
199
nodes ^= mask;
134
200
135
- result = result. max ( dfs ( input, to, seen | mask, cost + input . weight [ from ] [ to ] ) ) ;
201
+ result = result. max ( input . weight [ from ] [ to ] + dfs ( input, to, seen | mask) ) ;
136
202
}
137
203
138
204
result
0 commit comments