Interview problem with array of integers representing block towers, need to make stair-step pattern in minimum moves

Question:

Got this problem:

Having an array of integers ‘towers’ representing the height of some block towers (in number of blocks), need to write python code to arrange the towers into an ascending or descending stair-step pattern. This means the height of each tower should differ from the neighbors by exactly 1, and the whole sequence should be strictly increasing or decreasing. To change the towers, you can make multiple moves in which you add only one block to the top of any tower. Your task is to find the minimum number of moves required to make the towers either consecutively taller or shorter, whichever sequence requires fewer moves.

Example: input towers=[1,4,3,2] output is 4. Means "we need to add 4 blocks to the top of 1st tower, to get consecutively shorter array [5,4,3,2]"

Example 2: input towers=[5,7,9,4,11] output is 9. Means "add 2 blocks on top of 1st tower, +add 1 block on top of 2nd, +add 6 blocks on top of 4th tower", 9=2+1+6, to get final array [7,8,9,10,11]".

I didn’t find a way to solve it, please suggest.

Asked By: lugger1

||

Answers:

Original solution

Take towers=[5,7,9,4,11]. consider the reference diagonal [0,1,2,3,4]. The differences are [5,6,7,1,7]. So we must move the reference diagonal up by 7. It becomes [7,8,9,10,11]. Then it touches the skyline from above and the differences to the towers are [2,1,0,6,0]. And 2+1+0+6+0 is 9. Or instead of adjusting the 5 differences by 7, just adjust their sum by 5*7.

For [1,4,3,2] it’s the same, but we need a descending reference, [0,-1,-2,-3]. For each input, we try both ascending and descending, and find the better one.

for towers in [1,4,3,2], [5,7,9,4,11]:
    print(min(
        max(d)*len(d) - sum(d)
        for m in [-1,1]
        for d in [[t-m*i for i, t in enumerate(towers)]]
    ))

Output (Try it online!):

4
9

Probably easier

Instead of a descending reference diagonal, we can try an ascending one on the reversed towers. Also, here I first determine how far up we need to shift the reference diagonal, by finding the maximum tower - reference difference. And then I start the reference diagonal at that up and sum the reference - tower differences.

for towers in [1,4,3,2], [5,7,9,4,11]:
    print(min(
        sum(r-t for r, t in enumerate(ts, start=up))
        for ts in [towers, towers[::-1]]
        for up in [max(t-r for r, t in enumerate(ts))]
    ))

Constant space solution

Alternatively, also in linear time but with constant space, just do one pass over the towers, adjust the reference upwards whenever the current tower is higher, and keep track of the total tower increases. Just the ascending reference with the second example input:

towers = [5,7,9,4,11]
ref = total = 0
for i, tow in enumerate(towers):
    if tow > ref:
        total += i * (tow - ref)
        ref = tow
    total += ref - tow
    ref += 1
print(total)

Try it online!

Answered By: Kelly Bundy
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.