Problem overview
The method minCostClimbingStairs(cost) receives the price of each step of a staircase. Standing on step i costs cost(i) once you pay it, and from there you may climb one or two steps. You can start on step 0 or step 1, and you want the cheapest way to reach the top, which lies just past the last step. The array has between 2 and 1000 entries, each between 0 and 999.
For cost = [10, 15, 20] the answer is 15: start on step 1, pay 15, and take two steps to reach the top. For cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] the answer is 6. Starting on step 0 and stepping only on the cheap steps (indexes 0, 2, 4, 6, 7, 9) skips every 100.
Approach: the cheapest total for each step
Define the total for step i as the smallest amount you can have paid after paying for step i itself. You can only arrive at step i from step i - 1 or from step i - 2, so:
total(i) = cost(i) + min(total(i - 1), total(i - 2))
Steps 0 and 1 need no calculation, because you can start on either one: their totals are just their own costs. The top is one past the last step, and it can be reached from either of the last two steps without paying anything more, so the answer is the smaller of those two totals.
The lesson stores the totals in cost itself. Step i reads only the two entries before it, which have already been turned into totals, and then writes its own total over its price. No second array is needed.
For [10, 15, 20] the array becomes [10, 15, 30] (step 2 is 20 + min(15, 10) = 30), and the answer is min(30, 15) = 15.
Step by step
- Loop over the steps from index 2 to the last one with
2 until cost.length. Indexes 0 and 1 are left alone. - Add the cheaper predecessor to the current entry:
cost(i) += math.min(cost(i - 1), cost(i - 2)). After this line,cost(i)holds the cheapest total for standing on stepi. - Finish with the smaller of the last two totals,
math.min(cost(cost.length - 1), cost(cost.length - 2)), as the method's last expression.
The solution in Scala
class Solution {
def minCostClimbingStairs(cost: Array[Int]): Int = {
for (i <- 2 until cost.length) {
cost(i) += math.min(cost(i - 1), cost(i - 2))
}
math.min(cost(cost.length - 1), cost(cost.length - 2))
}
}
Some Scala details in this code:
2 until cost.lengthexcludes the upper bound, so the last index used iscost.length - 1. Withtothe loop would read past the end of the array.cost(i) += ...updates an array cell. Scala treats it ascost(i) = cost(i) + ..., and arrays are mutable, so the caller's array changes.- Arrays are passed by reference. The method modifies the array it was given. After
minCostClimbingStairs(Array(10, 15, 20))the caller's array holds10, 15, 30. If the original prices are still needed, pass a copy, for examplecost.clone. math.mintakes two numbers and returns the smaller one. It is available without an import.- The last expression is the result. There is no
return: the line withmath.minis the method's value.
Complexity
Time: O(n). One pass over the array, with constant work per step.
Space: O(1) extra. The totals overwrite the input, so no new array is made. The price is that the input is destroyed. Keeping the prices and tracking only the last two totals in two variables would also be O(1) and would leave the input intact.
Edge cases and common mistakes
- Two steps. The loop doesn't run, and the answer is the cheaper of the two.
[5, 5]gives 5. - Zero costs.
[0, 0, 0, 0]gives 0. - Returning the last total only. Returning
cost(cost.length - 1)forgets that the top can be reached from the second-to-last step. For[10, 15, 20]that would give 30 and not 15. - Adding a cost twice. The totals already include each step's price. Adding
cost(i)again in the final line would count the last price twice. - A one-element array. The notes promise at least 2 steps. With one element,
cost(cost.length - 2)iscost(-1)and throws anArrayIndexOutOfBoundsException. - Calling it twice on the same array. The second call would add totals on top of totals, because the array no longer holds the prices. Pass a fresh array each time.
- Overflow. The totals are at most 1000 * 999, about a million, so
Intis safe.
Testing
cost = [10, 15, 20]gives15.cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]gives6.cost = [5, 5]gives5.cost = [0, 0, 0, 0]gives0.
