746. Min Cost Climbing Stairs - Explanation

Problem Link

Description

You are given an array of integers cost where cost[i] is the cost of taking a step from the ith floor of a staircase. After paying the cost, you can step to either the (i + 1)th floor or the (i + 2)th floor.

You may choose to start at the index 0 or the index 1 floor.

Return the minimum cost to reach the top of the staircase, i.e. just past the last index in cost.

Example 1:

Input: cost = [1,2,3]

Output: 2

Explanation: We can start at index = 1 and pay the cost of cost[1] = 2 and take two steps to reach the top. The total cost is 2.

Example 2:

Input: cost = [1,2,1,2,1,1,1]

Output: 4

Explanation: Start at index = 0.

  • Pay the cost of cost[0] = 1 and take two steps to reach index = 2.
  • Pay the cost of cost[2] = 1 and take two steps to reach index = 4.
  • Pay the cost of cost[4] = 1 and take two steps to reach index = 6.
  • Pay the cost of cost[6] = 1 and take one step to reach the top.
  • The total cost is 4.

Constraints:

  • 2 <= cost.length <= 100
  • 0 <= cost[i] <= 100


Topics

Recommended Time & Space Complexity

You should aim for a solution as good or better than O(n) time and O(n) space, where n is the number of steps on the staircase.


Hint 1

Can you find the recurrence relation to solve the problem, given that at each step we have two options: going one step or two steps? Consider drawing a decision tree where we branch into two paths at each step. By exploring every path, we can get the minimum cost. However, this results in an O(2^n) time solution. Can you think of a better approach? Is there any repeated work in the decision tree that we can optimize?


Hint 2

The recurrence relation can be expressed as cost[i] + min(dfs(i + 1), dfs(i + 2)), where i is the current position and dfs is the recursive function. To avoid recalculating the result of a recursive call multiple times, we can use Memoization. Initialize a cache array of size n, where n is the number of steps on the staircase. How would you implement this?


Hint 3

We start the recursion from positions 0 and 1. At each recursive step, before computing the result, we check if the result for the current position has already been calculated. If it has, we return the stored value. Otherwise, we calculate the result for the current position, store it in the cache, and then return the result. What can be the base condition for this recursion to stop?


Hint 4

The base condition would be to return 0 if we are at the top of the staircase i >= n. This is a one-dimensional dynamic programming problem. We can further optimize the memoization solution by using advanced techniques such as Bottom-Up dynamic programming based on the recurrance relation.


Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Recursion - Breaking down problems into smaller subproblems and understanding base cases
  • Dynamic Programming - Recognizing overlapping subproblems and optimal substructure for memoization or tabulation
  • Arrays - Traversing and modifying array elements by index

1. Recursion

Intuition

From any step, you can climb 1 or 2 steps.
If you step on index i, you must pay cost[i], then choose the cheaper path ahead.
So the problem is: from each step, pick the minimum cost path to the top.

Algorithm

  1. Define a recursive function dfs(i) = minimum cost to reach the top starting from step i.
  2. If i is beyond the last step, cost is 0 (you reached the top).
  3. Otherwise:
    • Pay cost[i]
    • Choose the minimum of:
      • Jump 1 step → dfs(i + 1)
      • Jump 2 steps → dfs(i + 2)
  4. Since you can start from step 0 or 1, return:
    • min(dfs(0), dfs(1))
class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:

        def dfs(i):
            if i >= len(cost):
                return 0
            return cost[i] + min(dfs(i + 1), dfs(i + 2))

        return min(dfs(0), dfs(1))

Time & Space Complexity

  • Time complexity: O(2n)O(2 ^ n)
  • Space complexity: O(n)O(n)

2. Dynamic Programming (Top-Down)

Intuition

The brute force solution recomputes the same subproblems many times.
We can optimize it by remembering results once we compute them.

For each step i, the minimum cost to reach the top is:

  • cost[i] + minimum cost from step i+1 or i+2

By storing this result, we avoid repeated work.

Algorithm

  1. Create a memo array where memo[i] stores the minimum cost to reach the top from step i.
  2. Define dfs(i):
    • If i is beyond the last step, return 0.
    • If memo[i] is already computed, return it.
    • Otherwise:
      • memo[i] = cost[i] + min(dfs(i+1), dfs(i+2))
  3. Since you can start from step 0 or 1, return:
    • min(dfs(0), dfs(1))
class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        memo = [-1] * len(cost)

        def dfs(i):
            if i >= len(cost):
                return 0
            if memo[i] != -1:
                return memo[i]
            memo[i] = cost[i] + min(dfs(i + 1), dfs(i + 2))
            return memo[i]

        return min(dfs(0), dfs(1))

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n)

3. Dynamic Programming (Bottom-Up)

Intuition

Instead of solving the problem recursively, we build the answer from the bottom up.

Let dp[i] represent the minimum cost to reach step i.
To reach step i, you can:

  • Come from step i-1 and pay cost[i-1]
  • Come from step i-2 and pay cost[i-2]

We choose the cheaper of the two.

Algorithm

  1. Let n be the number of steps.
  2. Create a DP array dp of size n+1.
  3. Initialize:
    • dp[0] = 0, dp[1] = 0 (you can start at step 0 or 1 for free).
  4. For each step i from 2 to n:
    • dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])
  5. Return dp[n] as the minimum cost to reach the top.
class Solution:
    def minCostClimbingStairs(self, 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]

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n)

4. Dynamic Programming (Space Optimized)

Intuition

At each step, you only need the minimum cost of the next one or two steps.
So instead of using a full DP array, we can reuse the input array and update it in place.

Each cost[i] is updated to represent:

the minimum cost to reach the top starting from step i.

By the end, the answer is simply the minimum cost starting from step 0 or 1.

Algorithm

  1. Start from the third-last step and move backwards.
  2. For each step i:
    • Update cost[i] = cost[i] + min(cost[i+1], cost[i+2])
  3. After processing all steps:
    • Return min(cost[0], cost[1]) since you can start from either.
class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        for i in range(len(cost) - 3, -1, -1):
            cost[i] += min(cost[i + 1], cost[i + 2])

        return min(cost[0], cost[1])

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(1)O(1)

Common Pitfalls

Misunderstanding the Goal Position

The top of the staircase is at index n (one step past the last stair), not at index n-1. You need to reach beyond the last step, so your DP array or recursion must account for landing on position n, not just visiting the last cost element.

Forgetting You Can Start from Step 0 or Step 1

The problem allows starting from either step 0 or step 1 without paying any cost initially. A common mistake is assuming you must start from step 0 only, which can lead to suboptimal solutions when starting from step 1 would be cheaper.

Incorrect Base Cases in DP

When using bottom-up DP, the base cases should be dp[0] = 0 and dp[1] = 0 because you can stand on step 0 or step 1 for free before paying to move forward. Setting these incorrectly, such as dp[0] = cost[0], misinterprets when you pay the cost.