Expand description
§Many-Worlds Interpretation
Our high level approach is to simplify the problem into graph path finding. We only ever need to move directly from key to key, so the maze becomes a graph where the nodes are keys and the edge weight is the distance between keys. Doors modify which edges are connected depending on the keys currently possessed.
We first find the distance betweeen every pair of keys then run Dijkstra’s algorithm to find the shortest path that visits every node in the graph. The maze is also constructed in such a way to make our life easier:
- There is only ever one possible path to each key. We do not need to consider paths of different lengths that need different keys.
- As a corollary, if key
b
lies betweena
andc
then|ac| = |ab| + |bc|
. This enables a huge optimization that we only need to consider immediate neighbours. If we do not possess keyb
then it never makes sense to skip froma
toc
sinceb
is along the way. We can model this by treating keys the same as doors. This optimization sped up my solution by a factor of 30.
On top of this approach we apply some high level tricks to go faster:
- We store previously seen pairs of
(location, keys collected)
tototal distance
in a map. If we are in the same location with the same keys but at a higher cost, then this situation can never be optimal so the solution can be discarded. - When finding the distance between every pair of keys, it’s faster to first only find the immediate
neighbors of each key using a Breadth first search
then run the Floyd-Warshall algorithm
to contruct the rest of the graph. Even though the Floyd-Warshall asymptotic bound of
O(n³)
is higher than the asymptotic bounds of repeated BFS, this was twice as fast in practise for my input.
We also apply some low level tricks to go even faster:
-
The set of remaining keys needed is stored as bits in an
u32
. We can have at most 26 keys so this will always fit. For example needinga
,b
ande
is represented as10011
. -
Robot location is also stored the same way. Robots can only ever be in their initial location or at a key, so this gives a max of 26 + 4 = 30 locations. As a nice bonus this allows part one and part two to share the same code.
-
For fast lookup of distance between keys, the maze is stored as adjacency matrix.
a
is index 0,b
is index 1 and robots’s initial positions are from 26 to 29 inclusive. For example (simplifying by moving robot from index 26 to 2):######### [0 6 2] #b.A.@.a# => [6 0 4] ######### [2 4 0]
Structs§
- Door 🔒
distance
in the edge weight between nodes.needed
stores any doors in between as a bitfield. - Maze 🔒
initial
is the complete set of keys that we need to collect. Will always be binary11111111111111111111111111
for the real input but fewer for sample data. - State 🔒
position
andremaining
are both bitfields. For example a robot at keyd
that needsb
andc
would be stored asposition = 1000
andremaining = 110
.