The Lost Woods - Travelling Swordsman

Heart Pieces are abundant for our hero now, and nothing stands in his way to get them, for this time, our hero knows that each and every Heart Piece within the wood is accessible to him. However, all of this collecting has brought a want for efficiency into Link's mind. How can the hero move to collect every Heart Piece available using as few steps as possible?

This link (1) contains a zip file, with a single file in the Input Format specified for this collection of problems, of 1000 1000 mazes.

Let S S be the sum of the score for each maze. We define the score to be the index of the maze (1-based) multiplied by the minimum number of steps Link must take in order to collect every available Heart Piece in the maze. It is guaranteed that all of the Heart Pieces are accessible to Link.

Find S \boxed{S} .


Example:

Input:

3
6 1
H.L..H
3 3
H.H
.L.
H.H
7 7
...#...
.#...#.
.#####H
.##H..#
...##..
##..##.
L......

Output:

143

Output Explained:

In the first maze, Link could either go left and then right, or right and then left. In the former case, it would take 7 7 steps to collect both pieces, and in the latter, it would take 8 8 steps, so the score for the first maze is 1 × 7 1 \times 7 .

In the second maze, Link can go directly to any corner, then simply walk the perimeter of the wood to collect every Heart Piece. There is no one best route in this case, but that does not matter, for they all require the same number of steps: 8 8 .

In the third maze, once again, Link needs to choose which Heart Piece to go for before heading directly for the other one. Heading for the one in the middle turns out to be faster, and this yields a total of 40 40 steps.

1 × 7 + 2 × 8 + 3 × 40 = 143 1 \times 7 + 2 \times 8 + 3 \times 40 = 143


Details and Assumptions :

  • Link will always appear once per maze, as an 'L'.
  • There will always be at least 2, and at most 10 Heart Pieces. All of them will be accessible.
  • Open ground is given as '.'.
  • Impenetrable trees are given as '#'.
  • No other characters will be present in each maze.
  • 1 W , H 100 1 \leq W,H \leq 100 in all mazes.

    (1) - This link currently points to my Dropbox account, since there is currently no way to upload non-image content to Brilliant. Admin, feel free to delete this footnote if this is changed.


This is a part of the collection entitled The Lost Woods , a series of problems in CS / Graph Theory.

Image Credit: http://www.zeldalegends.net/gallery/thumbnails.php?album=735
Image Credit: http://zelda.wikia.com/wiki/Lost_Woods
Sources combined by me.


The answer is 72187125.

This section requires Javascript.
You are seeing this because something didn't load right. We suggest you, (a) try refreshing the page, (b) enabling javascript if it is disabled on your browser and, finally, (c) loading the non-javascript version of this page . We're sorry about the hassle.

1 solution

Daniel Ploch
Jan 15, 2016

The title of this problem is a major hint towards the means of its solution. Even so, some non-trivial reductions must be made to make the problem tractable.

The problem of 'What is the shortest path that can be taken to visit all of the nodes in a graph?' is known as the Travelling Salesman Problem , and it is known to be NP-hard , which doesn't bode well for mazes with up to ten-thousand nodes! Fortunately, only the number of Heart Pieces matters to the TSP part of the solution, and that is bounded in [ 2 , 10 ] [2, 10] , so an exponential-time solution is quite acceptable.

First, we need to pre-process the maze to produce a graph containing all the data we need to give to the travelling salesman portion. We need to compute the distances between Link and all of the Heart Pieces, as well as between all pairs of Heart Pieces. There are many ways to do this, but the simplest one I can think of, which builds on previous solutions, is to run Dijkstra's Algorithm for each of the important nodes, and save the distances to the other important nodes. This costs O ( W H log ( W H ) ) O(W \cdot H \cdot \log(W \cdot H)) time per iteration, so, letting N N be the number of Heart Pieces, it costs that N N times, and the resultant data requires O ( N 2 ) O(N^2) space.


Once we have all the distances computed, we can treat the problem as canonical TSP - what is the shortest path, starting from Link, that touches all of the Heart Piece nodes?

If you have a beefy computer and a lot of time to burn, you might get away with the naive solution of trying all permutations of the Heart Piece nodes and figuring out which order is the shortest, in O ( N ! ) O(N!) time, but this is not necessary. Using Dynamic Programming , the problem can instead be solved in O ( N 2 2 N ) O(N^2 \cdot 2^N) time, via the Held-Karp Algorithm .

To summarize, we first define a sub-problem to solve: Visit k k arbitrary Heart Pieces, ending at Heart Piece h h , in minimal time. There are 2 N 2^N subsets of Heart Pieces we could consider, and O ( N ) O(N) ending Heart Pieces to consider, and computing each sub-problem takes O ( N ) O(N) time, given that all previous sub-problems are already solved, hence the O ( N 2 2 N ) O(N^2 \cdot 2^N) runtime.


In Pseudocode:

# Run Dijkstra on each node to generate a map of [node, node] -> distance for the important nodes...

def visitation_state: { set[node] visited_nodes, node last_node }
tsp_cache = map{visitation_state, path_length}

def solve_tsp(vis_state):
  if vis_state in tsp_cache: return tsp_cache[vis_state]
  int val;
  if vis_state.visited_nodes == set[vis_state.last_node]: val = edge_weights[link, vis_state.last_node]
  else:
    val = INT_MAX
    for node in vis_state.visited_nodes: if node != vis_state.last_node:
      len = solve_tsp(visitation_state(vis_state.visited_nodes - set[vis_state.last_node], node))
      len += edge_weights[node, vis_state.last_node]
      if len < val: val = len
  tsp_cache[vis_state] = val
  return val

min = INT_MAX
for node in heart_pieces:
  len = solve_tsp(visitation_state(heart_pieces, node))
  if len < min: min = len
answer += index * min

Click this link to view the scores for each maze from the input in order, sans multiplication by the index. If you were not able to get the right answer, consulting these individual results may help you find the problem (or, if I got something wrong, the bug in my own solution!)

0 pending reports

×

Problem Loading...

Note Loading...

Set Loading...