150NeetCode 150

100.Min Cost Climbing Stairs

Easy1-D Dynamic Programming ยท 2/12

Problem

You are given an integer array cost where cost[i] is the price of stepping on stair i. After paying cost[i] you may climb either one or two stairs.

You may start on stair 0 or stair 1 for free (you still pay that stair's cost when you leave it). The "top" is the position just past the last stair, index n = cost.length. Return the minimum total cost to reach the top.

Examples

Input:  cost = [10, 15, 20]
Output: 15
Explanation: Start on stair 1, pay 15, climb two stairs to the top.
Input:  cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
Output: 6
Explanation: Start on stair 0 and step on 0, 2, 4, 6, 7, 9 โ€” paying 1 six times โ€” then climb to the top.

Constraints

  • 2 <= cost.length <= 1000
  • 0 <= cost[i] <= 999

๐Ÿ’ก Key Insight

Define f(i) = minimum cost to arrive at position i (not yet paying cost[i]). You arrive at i from i - 1 (paying cost[i - 1]) or from i - 2 (paying cost[i - 2]), so f(i) = min(f(i - 1) + cost[i - 1], f(i - 2) + cost[i - 2]) with f(0) = f(1) = 0 because you can start on either for free. The answer is f(n). Like Climbing Stairs, each state needs only the previous two, so two variables suffice.

Recursion (Brute Force)

TimeO(2โฟ)SpaceO(n)

Intuition

To stand at position i you made a final jump from i - 1 or i - 2. Try both, recursively compute the cheapest way to reach those earlier positions, and take the minimum.

Recurrence: f(i) = min(f(i - 1) + cost[i - 1], f(i - 2) + cost[i - 2]), f(0) = f(1) = 0.

Algorithm

  1. dfs(i): if i <= 1, return 0 (free starting stairs).
  2. Return min(dfs(i - 1) + cost[i - 1], dfs(i - 2) + cost[i - 2]).
  3. Answer is dfs(n).

Visualization

cost = [10, 15, 20, 5], n = 4. f(2) appears twice and f(1)/f(0) many times.

flowchart TD
    A["f(4)"] -->|"+cost[3]=5"| B["f(3)"]
    A -->|"+cost[2]=20"| C["f(2)"]
    B -->|"+cost[2]=20"| D["f(2)"]
    B -->|"+cost[1]=15"| E["f(1) = 0"]
    C -->|"+cost[1]=15"| F["f(1) = 0"]
    C -->|"+cost[0]=10"| G["f(0) = 0"]
    D -->|"+cost[1]=15"| H["f(1) = 0"]
    D -->|"+cost[0]=10"| I["f(0) = 0"]

f(2) = 10, f(3) = min(10 + 20, 0 + 15) = 15, f(4) = min(15 + 5, 10 + 20) = 20.

Code

java
class Solution {
    public int minCostClimbingStairs(int[] cost) {
        return dfs(cost, cost.length);
    }

    // minimum cost to arrive at position i
    private int dfs(int[] cost, int i) {
        if (i <= 1) {
            return 0; // may start on stair 0 or 1 for free
        }
        int fromOne = dfs(cost, i - 1) + cost[i - 1];
        int fromTwo = dfs(cost, i - 2) + cost[i - 2];
        return Math.min(fromOne, fromTwo);
    }
}

Complexity

  • Time: O(2โฟ) โ€” two recursive branches per call.
  • Space: O(n) โ€” recursion depth.

Complexity Comparison

Approach Time Space
Recursion (Brute Force) O(2โฟ) O(n)
Top-Down Memoization O(n) O(n)
Bottom-Up Tabulation O(n) O(n)
Space-Optimized DP O(n) O(1)

Common Pitfalls

  • Returning dp[n - 1] โ€” the top is past the last stair, at index n.
  • Mixing up the two definitions: "cost to arrive at i" (pay the stair you leave) vs "cost to leave i" (dp[i] = cost[i] + min(dp[i-1], dp[i-2]), answer min(dp[n-1], dp[n-2])). Both work, but don't blend them.
  • Forgetting that both stair 0 and stair 1 are free starting points โ€” dp[1] is 0, not cost[0].
  • Using 0 as the "not computed" sentinel in memoization: 0 is a valid answer, so use -1.