How to place midgets for leapfrog?

I recently practiced my problem-solving skills in preparation for a programming competition in high school and came across an interesting problem, Awesome Frog

The inaugural Frogleaping International Olympiad is held in Australia in 2013 and you are determined to win. Although you do not want to have anything to do with such slippery, pimple creatures, you plan to introduce a frog robot, which, as you know, will be faster than all other organic participants.

IOF takes place in a large pond, in which there is a sequence of lilies laid in a long line. The rules of the race are simple: your frog will be placed on the first lily pad, then it must jump to the second lily pad, then to the third and so on until it reaches the last lily in the course. Please note that you cannot skip lily pads - you need to jump each lily cube exactly once. The first frog, which reaches the last lily cubes, will win the race. Since your robotic frog has super-frog speed, you are confident in your victory.

However, your frog has one minor wrong flaw - it can only jump one fixed distance. In particular, he can jump exactly K meters ahead from his current location, even if it makes the frog in the water (where it will quickly lock itself).

Since the initial positions of the lily pads can cause your frog to not reach the last panel of the lily, you plan to create a distraction and move the lily pads so that they are located exactly at a distance of K meters, allowing your frog to jump from the first to the last, without falling into the water. Shifting the lily pad by one meter will take you one second, and the longer you spend the inconspicuously moving lily pads, the more likely the IOF judges will notice and disqualify you from the competition.

Given the initial distances between the lily pads in the course, you should write a program to calculate the minimum time you have to spend on transferring the lily pads so that all pairs of consecutive lilies are exactly at a distance of K meters. You can assume that the pond is long enough so that the first lily pad can be moved any distance backward, and the last lily pad can be moved any distance forward.

Input and output example at http://orac.amt.edu.au/cgi-bin/train/problem.pl?set=aio12sen&problemid=632


I have been thinking about this for a long time and hard, and I still can’t think of any way to accomplish this given the time constraints or even without taking this into account. Any recommendations on how to approach this or any suggestions on research algorithms / concepts will be greatly appreciated. Thank you in advance.

Any code would be appreciated, preferably in c / C ++, python, Java or C #. thanks

+7
performance language-agnostic algorithm
source share
3 answers

Your choice of where to position the first lillipard completely determines the positions of other lilies (since they should all be spaced K distance). For this choice, the cheapest strategy is obvious: keep the lipids in their original order (because any strategy in which two lipids are transferred can be improved without transferring them).

The task is to minimize f(i) , where i is the final position of the first lily, and the function f is the cost of moving all N lilies that will separate from K.

Computing f for a given i is computationally quick - it is just arithmetic (we derive the end positions of each lily from the “no transposition” observation and summarize the differences with the initial positions).

We could minimize f by looking for brute force on all i , but this is likely to be too slow (of course, too slow, since this is a competition of algorithms). Therefore, we need to more intelligently seek space. Binary search? No, not really, because it requires us to know what value we are looking for, and f to be monotonous.

What does f look like? Think about it.

The function f is convex (U-shaped). What for? This follows from our “no transposition” observation. For any choice, I will need to drag some lilies to the right, others to the left. If more lilies need to be dragged right to the left, then we can improve f by moving the empty lily i (and the rest) one step to the right (f will decrease by the difference). Finally, note that the number of lilies that need to be moved to the right is a decreasing function of i, i.e. F ''> = 0. We have proved that f is convex.

Now we look or come up with a (funny) fast binary search algorithm to minimize common convex functions and apply it. Or simply, we can use the bog-standard binary search to solve f '(i) = 0, where f' is the difference between the number of lilies that need to be dragged in each direction.


Remember, before writing any code, solve the problem on paper. Programming is a distraction from thinking about a problem.

 def solve(startings, K): N = len(startings) def ends(start): stop = start + N*K endings = range(start, stop, K) assert len(endings) == N return endings def f(start): endings = ends(start) return sum(abs(xy) for (x,y) in zip(startings, endings)) def f_prime(start): endings = ends(start) cost = sum(cmp(x,y) for (x,y) in zip(startings, endings)) return cost lower = min(startings) - N*K upper = max(startings) + N*K g = lambda x: -1 * f_prime(x) stationary_point = binary_search(g, lower, upper) i_best = min(stationary_point, stationary_point + 1, key = f) return f(i_best) def binary_search(f, lower, upper): """Find the greatest integer n, lower <= n <= upper, such that f(n) <= 0. Assumes f is increasing.""" assert upper >= lower assert f(upper) >= f(lower), "f must be increasing" assert f(upper) >= 0 >= f(lower), "f must have a root in range" while upper > lower+1: mid = (upper + lower) // 2 if f(mid) <= 0: lower = mid else: upper = mid return upper if f(upper) <= 0 else lower # unit tests assert binary_search(lambda x: x - 4, 0, 6) == 4 assert binary_search(lambda x: x**2 - 5, 0, 10) == 2 assert binary_search(lambda x: x, 0, 6) == 0 assert binary_search(lambda x: x-6, 0, 6) == 6 if __name__ == "__main__": import fileinput f = fileinput.input() N, K = [int(x) for x in f.readline().split()] gaps = [int(f.readline()) for i in range(N-1)] startings = [0] for gap in gaps: startings.append(startings[-1] + gap) assert len(startings) == N print solve(startings, K) 
+2
source share

In general, you can think of a solution to the problem in terms of modeling and design / implementation.

  • Repeat the problem in your own words . In a scientific or mathematical context, you should use mathematical notation where possible to make the definition accurate and concise.
  • Identify possible operations . This is similar to a typical approach to computer science, where you identify possible operations by an abstract data type or events that can enter and exit your system.
  • Based on 1 and 2, create an algorithm .
  • Check and return to step 3 if necessary, or even to steps 1 or 2 if you find errors in the statement or operations of the problem — or ways to repeat the problem and identify operations to simplify the implementation.

The simulation corresponds to steps 1 and 2, and the design / implementation corresponds to steps 3 and 4.

Since this is a practice problem for competitions in high school, and you asked how to handle it, I will try to do 1 and 2 and give some hints at 3. I will leave 4 soley to you: I find it difficult to complete steps 1 and 2 without errors, therefore, although there may be some errors, I hope that the following will be useful to indicate how you could proceed.

1 Description of the problem

You have circles n , and you can go forward by the distance x (whose values ​​are defined in your input file)

  • Let circles n denoted by the numbers 0 - (n-1)

  • Let d[i], 1 ≤ i ≤ n-1 , the distance between the circle (i-1) and the circle i . (Whose initial values ​​are defined in your input file)

The goal is to achieve: for all i , d[i] = x , using the minimum number of movements of the circle, where each movement moves the circle 1 distance to the left or right.

2 Possible operations

For the first and last laps

  • Move the first circle, circle 0 left by y spaces: d[1] = d[1] + y

  • Move the first circle, circle 0 to the right by y spaces: d[1] = d[1] - y

  • Move the last circle, circle n-1 left to y spaces: d[n-1] = d[n-1] - y

  • Move the last circle, circle n-1 to the right, spaces y : d[n-1] = d[n-1] + y

For any intermediate circles i , 1 ≤ i ≤ n - 1

  • Move the circle i left by y spaces: d[i] = d[i] - y and d[i+1] = d[i+1] + y

  • Move the circle i to the right by y spaces: d[i] = d[i] + y and d[i+1] = d[i+1] - y

Summarizing

  • for the first and last distances you can add / subtract any number y
  • for the middle distances you can add / subtract any number y , but you have to balance this by subtracting / adding y from the adjacent distance

3 Create an algorithm

There are many different approaches to developing an algorithm, but one of the best is to start with simple examples, translate them into your model, solve the model, and see if you can generalize it. This is the approach that I would use here: once I solved simple examples, I would throw more unpleasant, extreme cases on my algorithm, until I was sure that I had a problem.

Other approaches I've taken from Cracking the Coding Interview include

  • Reuse the wheel. See if you can find a similar problem. Either turn your problem into a different problem and use a different solution to the problem, or just change your problem enough to see how it can be solved.

  • Simplify the problem, and then gradually create your problem again. Make an assumption to alleviate your problem. Solve with this assumption, then remove the assumption and change your simple solution to enable the re-introduced function. Continue until you solve your original problem. An example would be a solution to your problem with the restriction that the sum of the distances starts as (n-1) * x , and you are not allowed to move the first or last circles.

  • Base unit and assembly. Decide on one distance, then two distances, then three distances. This can lead to an obvious pattern or to a recursive solution where you can write your problem for distances n in terms of the problem for distances n-1 .

  • Data structure and algorithms brainstorm aka British Museum Algorithm. Think of all the informatics structures and algorithms that may be relevant, and see if any work is working. Upward approach!

Good luck

+3
source share

You enter an array (with one 0 added to it), which should be converted into an array with the same size N with elements indicating the relative distance that each lily pillow should move if the first lily pillow should not move at all, So, from the array 0 8 3 6 4 we have 0 -2 1 1 3 . From this array you have to make another 2 * X array whose elements mean how many elements in the original array matter with the element index. This array has negative indices (-X + 1, X-1), but in programming you can use an offset and just use a regular array (0, 2X-1).

In our case, this new array is' 0 0 0 1 0 1 2 0 1 0 0`.

Now you only need to shift this array in one or the other direction, until the sum of the absolute indices multiplied by the values ​​becomes minimal. The current amount is 1 * | -2 | + 1 * 0 + 2 * 1 + 1 * 3 = 7. Since the sum including negative indices is less than the sum including positive, you must move the array to negative (to the left). The algorithm must support both the left and right sum, as well as the sum of the values ​​involved in these sums, as well as the index of the "current zero value". In our case, the algorithm starts with left_sum=2 , left_count=1 , right_sum=5 , right_count=3 and current_zero_index=0 . The result of the algorithm is total_sum , initially left_sum + right_sum . Each time you shift to the right, you adjust these five values:

 new_left_sum = left_sum + left_count + array[current_zero_index] new_left_count = left_count + array[current_zero_index] new_right_sum = right_sum - right_count new_right_count = right_count - array[current_zero_index] current_zero_index = current_zero_index + 1 

After that, if new_left_sum + new_right_sum greater than some total_sum , then the current total_sum is the solution. Otherwise, total_sum becomes that amount, and you repeat the process.

The left shift is similar (but not quite the same).

0
source share

All Articles