Min Cost Climbing Stairs – Solution & Complexity

Solution Walkthrough

1. Frame the recurrence

  • Let best[i] be the cheapest toll paid to arrive at stair i.
  • Arriving at the top means arriving at index n, one past the last stair.
  • You reach stair i from either stair i-1 or stair i-2, paying that stair's toll when you leave it.

2. Tabulate over a DP array

  • best[0] = best[1] = 0 because you may start on either stair for free.
  • For every later index, take the cheaper of the two incoming moves and add the toll you paid to make that move.
  • The answer is best[n].
def min_cost_climbing_stairs(cost: list[int]) -> int:
    n = len(cost)
    dp = [0] * (n + 1)
    for i in range(2, n + 1):
        dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])
    return dp[n]

3. Collapse to two rolling variables

  • The recurrence only ever looks back two positions, so a full array is unnecessary.
  • Track downOne (cost to reach the previous stair) and downTwo (the stair before that) and slide them forward.

4. Optimal O(1)-space scan

  • Iterate from index 2 through n, computing each new arrival cost from the two rolling variables, then shift them.
  • After the loop, downOne holds the cost to reach the top.
def min_cost_climbing_stairs(cost: list[int]) -> int:
    down_two, down_one = 0, 0
    for i in range(2, len(cost) + 1):
        step = min(down_one + cost[i - 1], down_two + cost[i - 2])
        down_two, down_one = down_one, step
    return down_one

5. Dry run

Trace cost = [10,15,20].

idownTwodownOnenew step
200min(0+15, 0+10) = 10
3010min(10+20, 0+15) = 15

The loop ends with downOne = 15, matching the expected answer.

6. Common mistakes and follow-ups

  • Returning dp[n-1] instead of dp[n], which stops one stair short of the top.
  • Adding cost[i] when arriving instead of cost[i-1]/cost[i-2] for the stair you leave.
  • Forgetting that both stair 0 and stair 1 are free starting points.
  • Follow-up: how would the recurrence change if you could also climb three stairs at a time?

7. Edge cases to test mentally

  • The minimum length is 2; the answer is then min(cost[0], cost[1]).
  • All-zero tolls cost 0.
  • Alternating free and expensive stairs should always land on the free ones.

8. Final full solution and complexity

A single left-to-right scan keeps two rolling subresults. Time is O(n) and extra space is O(1).

def min_cost_climbing_stairs(cost: list[int]) -> int:
    down_two, down_one = 0, 0
    for i in range(2, len(cost) + 1):
        step = min(down_one + cost[i - 1], down_two + cost[i - 2])
        down_two, down_one = down_one, step
    return down_one

FAQ