Editorial for APIO '13 P1 - Robots
Submitting an official solution before solving the problem yourself is a bannable offence.
Solution
Here is a quadratic solution that uses dynamic programming in a naive way.
We first pre-compute the minimum number of pushes needed to move any robot from one grid to another. We can solve this as an all-pair shortest path problem on a directed, unweighted, graph , where every vertex is a grid and an edge exists if a single push would move the robot from to . Denote as the minimum number of pushes to send a robot from to . if it is not possible to reach from . This step can be computed in time. In the graph we constructed, each vertex has at most four outgoing edges (one for each direction). Therefore, and this step takes time.
Note that we only need to keep track of grid locations that are reachable by the robots. In the worst case, is but in practice, if the number of occlusions is small, .
We then compute the following: Given two compatible robots at grids and , how many pushes are needed if they are to merge in location ? The number of pushes is the sum of the minimum cost from to and to . The order we push the robots does not matter.
Next, we need to determine the order of merging. The order can be modelled as a binary tree with the individual robots as leaf nodes and composite robots as intermediate nodes. The solution is thus to find, among all possible binary trees of nodes, the tree that requires the fewest number of pushes. The solution can be achieved with dynamic programming.
Let be the minimum number of pushes it takes to merge robots to , with the final merge taking place at grid . The final answer to the task ROBOTS is .
Let be the minimum number of pushes it takes to merge robots to in some grid AND move it to grid . In other words,
Here is the dynamic programming solution. First, we consider the base case, and initialize to if robot is located at grid initially.
For the recursive case, we have
for .
Translating the equations above directly, we have our first solution, presented in Algorithm 1. The algorithm runs in time, with Line 19 and computation of being the bottleneck. Note that since , we treat the factor in the running time as a constant.
(too lazy to copy Algorithm 1)
Solution
To improve the running time, we need to find a faster way to compute and avoid computing . The key insight is that if has not been determined, and is reachable from some grid location in one push (i.e., ), then . Further, we know that if and merge at , then .
For ease of exposition, we abuse the notation of to represent "minimum-so-far" in the following explanation and pseudocode, instead of the minimum.
The improved algorithm to compute is similar to that of Dijkstra's algorithm. We say that we relax a vertex if we set to when and . We maintain a priority queue containing the "front" of vertices we are exploring, initialized with vertices corresponding to the merged locations. The main loop expands the front and keeps relaxing vertices until there are no more vertices to relax. When this happens, we know that each of the already contains its minimum value.
Note that this algorithm to compute no longer makes use of . It turns out that we do not require during initialization as well, as can be initialized by repeatedly relaxing the vertices. The removal of removes the bottleneck from the algorithm.
The pseudocode for this improved algorithm is shown in Algorithm 2. Note that the looping condition at Line 11 has changed to accommodate the calculation of .
In the worst case, each vertex is inserted into the queue once for each incoming neighbor. Thus, there are insertions, each taking time using a binary heap implementation of a priority queue. Since , the running time for this version of the algorithm takes time.
Solution
We now present the final solution, relying on the observations that: (O1) the value of is bounded and is an integer, and (O2) we always insert an item with a key larger than the current minimum in Line 29.
Let's begin by bounding the value of . We will present a very loose bound here for simplicity. For any two robots and to merge, in the worst case, they must (collectively) visit every reachable grid (each requiring one push). After merging, the composite robot may be pushed to each of the reachable grid again, before reaching . This simple analysis gives an upper bound for for any , and , as .
Since the key value of the priority queue is an integer and is bounded, we can implement the priority queue as (i) an array of lists , where stores a list of grid locations such that , and (ii) a value , such that is the non-empty list with the smallest index.
The operation (Lines 20 and 29) simply appends an item to the end of the list and updates if necessary. This operation takes time. The operation (Line 25) simply returns an item from the list and scans for the next higher if becomes empty. Observation O2 implies that it suffices to make a single pass through the list (i.e., always increases inside the while loop starting at Line 24). Summing up the cost of every operation, the running time is . Therefore, with this implementation of the priority queue, the total running time for the algorithm is reduced to .
(too lazy to copy Algorithm 2)
Credits
Task statement and solution by Wei Tsang Ooi. solution by Harta Wijaya. solution by Raymond Kang and Harta Wijaya.
Comments