Min Cost Climbing Stairs is the gentlest possible introduction to bottom-up dynamic programming. There are no fancy data structures — just an array you fill left to right, where each entry is built from the two entries before it.
Problem. You are given an array cost where cost[i] is what you pay to step off stair i. You may
start on stair 0 or stair 1, and from any stair you may climb one or two stairs. Return the
minimum total cost to reach the top (just past the last stair).
Example: cost = [10, 15, 20] → answer 15 (start on stair 1, pay 15, climb two stairs to the top).
The slow way first
The brute-force idea is recursion: from the top, ask "did I arrive from the stair below or two below?" and try both, recursing each time. But the same stair gets recomputed over and over, giving an exponential O(2ⁿ) blow-up. The recursion tree overlaps badly — that overlap is the signal to switch to DP.
The question to ask: what is the cheapest cost to reach each stair, one stair at a time? If I already know the answer for the two stairs below me, my own answer is one min away.
The idea: build a dp table
Let dp[i] be the minimum cost to reach stair i. You arrive at i either from i-1 (paying cost[i-1] to leave it) or from i-2 (paying cost[i-2]). So:
dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])
The base cases are free: dp[0] = dp[1] = 0, because you may start on either stair without paying yet. The top is index n, so the answer is dp[n].
The key insight: because we fill dp left to right, the two values we need are always already computed by the time we reach i.
Walk through it
Step through the animation. The top row is cost; the bottom row is dp. We fill dp[0] and dp[1] to 0, then the pointer i moves right. At each cell we compare the two ways to arrive and keep the cheaper. For cost = [10, 15, 20], dp ends as [0, 0, 10, 15], and the last cell 15 is the answer.
Pseudocode
n = number of stairs
make dp of size n+1, all zero # dp[0] and dp[1] start free
for i from 2 to n:
arrive_from_one = dp[i-1] + cost[i-1]
arrive_from_two = dp[i-2] + cost[i-2]
dp[i] = min(arrive_from_one, arrive_from_two)
return dp[n] # cheapest cost to the topThe Python solution
def min_cost_climbing_stairs(cost):
n = len(cost)
dp = [0] * (n + 1)
# dp[0] and dp[1] are free starting points
for i in range(2, n + 1):
step1 = dp[i - 1] + cost[i - 1]
step2 = dp[i - 2] + cost[i - 2]
dp[i] = min(step1, step2)
return dp[n]dphasn + 1slots: one per stair plus the top at indexn.dp[0]anddp[1]stay0— starting on either stair is free.- The loop fills the rest left to right; by the time we reach
i, bothdp[i-1]anddp[i-2]are known. step1arrives from the stair just below;step2arrives from two stairs below.dp[i] = min(step1, step2)keeps the cheaper route, anddp[n]is the final answer.
Complexity
| Case | Time | Notes |
|---|---|---|
| Naive recursion | O(2ⁿ) (moderate) | recomputes overlapping subproblems |
| Bottom-up dp (this solution) | O(n) (moderate) | one pass, O(1) work per stair |
O(n) (moderate)We do constant work at each of the n stairs, so the fill is O(n) time and O(n) space for the table. (You can shrink space to O(1) by keeping just the last two values instead of the whole array.)
When this pattern shows up
Whenever an answer for position i depends only on a couple of earlier positions, reach for a
bottom-up dp array. Climbing Stairs, House Robber, Fibonacci, and Min Cost Climbing Stairs are all the
same move: define dp[i], write a recurrence from earlier cells, then fill left to right.
Mind the indices. The dp array has length n + 1, and cost[i-1] (not cost[i]) is what you pay to
arrive at dp[i]. Off-by-one here is the most common bug in this problem.
Practice
For cost = [10, 15, 20], you have dp = [0, 0, 10] so far. What is dp[3], the cost to reach the top?
1. What does dp[i] represent?
2. Why are dp[0] and dp[1] set to 0?
3. Why is filling dp left to right safe?
4. What is the answer the function returns?