Problem overview
The function minCostClimbingStairs(cost) receives an IntArray where cost[i] is the price of stepping on stair i. After paying a stair's cost you may climb one or two stairs. You can start on stair 0 or on stair 1, and the goal is the top, which is one position past the last stair. The function returns the minimum total cost to get there.
For cost = [10, 15, 20] the answer is 15: start on stair 1, pay 15, and jump two stairs to the top. For cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] the answer is 6: stand only on the cheap stairs, using steps of two over each 100 and skipping the 1 at index 3. The statement says the array has between 2 and 1000 entries, each between 0 and 999.
Approach: cheapest total to stand on each stair
You can reach stair i only from stair i - 1 or stair i - 2, so the cheapest way to stand on it is the cheaper of those two totals, plus the stair's own cost. Stairs 0 and 1 are starting points, so their totals are just their own costs. From there every other total follows from the two before it.
The top is not a stair. It can be reached from the last stair (one step) or the second-to-last (two steps), so the final answer is the cheaper of those two totals. Standing on the last stair is not required: stepping from the second-to-last directly to the top skips paying for the last one.
This lesson stores the totals in cost itself. When the loop reaches stair i, the entries before it already hold totals, so the update cost[i] += minOf(cost[i - 1], cost[i - 2]) turns the stair's price into its total. That avoids a second array.
Step by step
- Loop
ifrom 2 untilcost.size. Stairs 0 and 1 need no update, because you may start on either of them. - Add the cheaper previous total.
cost[i]grows byminOf(cost[i - 1], cost[i - 2]), and afterwards it holds the cheapest total to stand on stairihaving paid for it. - Return the cheaper of the last two totals,
cost[cost.size - 1]andcost[cost.size - 2], since the top is reachable from either.
The solution in Kotlin
class Solution {
fun minCostClimbingStairs(cost: IntArray): Int {
for (i in 2 until cost.size) {
cost[i] += minOf(cost[i - 1], cost[i - 2])
}
return minOf(cost[cost.size - 1], cost[cost.size - 2])
}
}
The Kotlin details worth a closer look:
- Arrays are passed by reference. The function receives the caller's
IntArray, not a copy, socost[i] += ...changes the caller's array. AfterminCostClimbingStairs(intArrayOf(10, 15, 20))on a variablec,cholds[10, 15, 30]. If the caller still needs the original prices, passcost.copyOf()or use a separate table. - Parameters can't be reassigned, but elements can. A Kotlin function parameter is read-only, so
cost = ...would not compile, yetcost[i] = ...is fine because it changes the array's contents, not which arraycostnames. until.2 until cost.sizestops at the last valid index.2..cost.sizewould run once more and throwArrayIndexOutOfBoundsException.minOf. It is the standard-library function that returns the smaller of its arguments.- Compound assignment on elements.
cost[i] += xreads the element, addsxand stores the result back.
Complexity
Time: O(n). One pass over the array, with constant work per stair.
Space: O(1) extra. The totals are written into the input array. If you keep the input intact with a copy, the space becomes O(n). Two variables holding the previous two totals would also do it in O(1) without changing the input.
Edge cases and common mistakes
- Two stairs.
[5, 5]returns 5. The loop doesn't run, and the answer is the cheaper of the two starting stairs. - Zero costs.
[0, 0, 0, 0]returns 0. - Returning the last total only. For
[10, 15, 20]the last total is 30, but the answer is 15. The top can be reached from the second-to-last stair. - Looping from 0 or 1. That reads
cost[-1]and throws an exception. - Calling the function twice on the same array. The first call overwrites the costs with totals, so the second call starts from the wrong values.
- Adding the wrong neighbour. Using
maxOfinstead ofminOffinds the most expensive route.
Testing
[10, 15, 20]gives15.[1, 100, 1, 1, 1, 100, 1, 1, 100, 1]gives6.[5, 5]gives5.[0, 0, 0, 0]gives0.