746. Min Cost Climbing Stairs - Explanation
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: 2Explanation: 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: 4Explanation: Start at index = 0.
- Pay the cost of
cost[0] = 1and take two steps to reach index =2. - Pay the cost of
cost[2] = 1and take two steps to reach index =4. - Pay the cost of
cost[4] = 1and take two steps to reach index =6. - Pay the cost of
cost[6] = 1and take one step to reach the top. - The total cost is
4.
Constraints:
2 <= cost.length <= 1000 <= 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.
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
- Define a recursive function
dfs(i)= minimum cost to reach the top starting from stepi. - If
iis beyond the last step, cost is0(you reached the top). - Otherwise:
- Pay
cost[i] - Choose the minimum of:
- Jump
1step →dfs(i + 1) - Jump
2steps →dfs(i + 2)
- Jump
- Pay
- Since you can start from step
0or1, return:min(dfs(0), dfs(1))
Time & Space Complexity
- Time complexity:
- Space complexity:
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 stepi+1ori+2
By storing this result, we avoid repeated work.
Algorithm
- Create a
memoarray wherememo[i]stores the minimum cost to reach the top from stepi. - Define
dfs(i):- If
iis beyond the last step, return0. - If
memo[i]is already computed, return it. - Otherwise:
memo[i] = cost[i] + min(dfs(i+1), dfs(i+2))
- If
- Since you can start from step
0or1, 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:
- Space complexity:
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-1and paycost[i-1] - Come from step
i-2and paycost[i-2]
We choose the cheaper of the two.
Algorithm
- Let
nbe the number of steps. - Create a DP array
dpof sizen+1. - Initialize:
dp[0] = 0,dp[1] = 0(you can start at step0or1for free).
- For each step
ifrom2ton:dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])
- Return
dp[n]as the minimum cost to reach the top.
Time & Space Complexity
- Time complexity:
- Space complexity:
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
- Start from the third-last step and move backwards.
- For each step
i:- Update
cost[i] = cost[i] + min(cost[i+1], cost[i+2])
- Update
- After processing all steps:
- Return
min(cost[0], cost[1])since you can start from either.
- Return
Time & Space Complexity
- Time complexity:
- Space complexity:
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.
Sign in to join the discussion