Min Cost Climbing Stairs
#include <bits/stdc++.h> // Includes common standard libraries like iostream, vector, algorithm (for min).using namespace std; // Use the standard namespace.
// 1. Recursive Approach (Brute Force)// Calculates the minimum cost to reach stair 'i'.// Time Complexity: O(2^N) - Exponential, due to repeated calculations.// Space Complexity: O(N) - Due to recursion stack depth.// 'stairs': Vector containing costs for each stair.// 'i': Current stair index being considered.int minCostRec(vector<int> &stairs, int i) { // Renamed for clarity in main // Base Case 1: If index is less than 0, it means we are before the stairs. Cost is 0. if(i < 0) { return 0; } // Base Case 2: If index is 0 or 1, the cost to reach this stair is just its own cost. // (Since we can start from 0 or 1, and assume we've "arrived" there). if(i <= 1) { return stairs[i]; }
// Recursive step: To reach stair 'i', we can come from (i-1) or (i-2). // The cost is stairs[i] + minimum cost to reach either (i-1) or (i-2). int cost_from_prev1 = minCostRec(stairs, i-1); // Cost if we came from stair (i-1) int cost_from_prev2 = minCostRec(stairs, i-2); // Cost if we came from stair (i-2)
return stairs[i] + min(cost_from_prev1, cost_from_prev2);}
// 2. Dynamic Programming - Top-Down Approach (Recursion + Memoization)// Optimizes the recursive solution by storing computed results in 'dp' table.// Time Complexity: O(N) - Each subproblem is computed once.// Space Complexity: O(N) for 'dp' vector + O(N) for recursion stack = O(N) total.// 'stairs': Vector containing costs.// 'i': Current stair index.// 'dp': Memoization table, initialized with 0 (or -1) indicating not computed.int minCostMemo(vector<int> &stairs, int i, vector<int> &dp) { // Renamed for clarity if(i < 0) { // Base Case 1 return 0; } if(i <= 1) { // Base Case 2 return stairs[i]; }
// If the result for 'i' is already computed, return it from dp table. if(dp[i] != 0) return dp[i]; // Assuming 0 is not a valid cost, or use -1.
// Compute the result and store it in dp table before returning. dp[i] = stairs[i] + min(minCostMemo(stairs, i-1, dp), minCostMemo(stairs, i-2, dp));
return dp[i];}
// 3. Dynamic Programming - Bottom-Up Approach (Tabulation)// Builds the solution iteratively from base cases up to 'n-1'.// Time Complexity: O(N) - Single loop.// Space Complexity: O(N) - For the 'dp' vector.// 'stairs': Vector containing costs.int minCostTab(vector<int> &stairs) { // Renamed for clarity int n = stairs.size();
// Handle edge cases for n (number of stairs) if (n == 0) return 0; // No stairs, no cost. if (n == 1) return stairs[0]; // Only one stair, cost is just that stair's cost.
// Create a dp table of size 'n'. dp[i] will store the minimum cost to reach stair 'i' (and pay its cost). vector<int> dp(n); // Initialize base cases for dp table. dp[0] = stairs[0]; dp[1] = stairs[1];
// Iterate from stair 2 up to the last stair (n-1). for(int i = 2; i < n; i++) { // The cost to reach stair 'i' is its own cost plus the minimum cost to reach either (i-1) or (i-2). int prevCost = min(dp[i-1], dp[i-2]); dp[i] = stairs[i] + prevCost; }
// The final answer is the minimum cost to reach the 'top' (after the last stair). // This means comparing the cost to reach (n-1)th stair and then step, // or the cost to reach (n-2)th stair and then step. return min(dp[n-1], dp[n-2]);}
// 4. Dynamic Programming - Space Optimization Approach// Optimizes the bottom-up approach to use only constant space.// Time Complexity: O(N) - Single loop.// Space Complexity: O(1) - Uses only a few variables.// 'stairs': Vector containing costs.int minCostSpaceOpt(vector<int> &stairs) { // Renamed for clarity int n = stairs.size();
// Handle edge cases for small 'n'. if (n == 0) return 0; if (n == 1) return stairs[0];
// 'prev0' stores the minimum cost to reach stair (i-2). // 'prev1' stores the minimum cost to reach stair (i-1). // Initialize for i=2: prev0 for stair 0, prev1 for stair 1. int prev0 = stairs[0]; // Corresponds to dp[0] int prev1 = stairs[1]; // Corresponds to dp[1]
// Iterate from stair 2 up to the last stair (n-1). for(int i = 2; i < n; i++) { // 'curr' calculates the minimum cost to reach current stair 'i'. int curr = stairs[i] + min(prev0, prev1);
// Update for the next iteration: // 'prev0' becomes the old 'prev1' (now cost for (i-1)). // 'prev1' becomes the current 'curr' (now cost for 'i'). prev0 = prev1; prev1 = curr; }
// The final answer is the minimum of the cost to reach the last two stairs. // This is because you can reach the "top" from either stair (n-1) or stair (n-2). return min(prev0, prev1); // 'prev0' holds cost for (n-2), 'prev1' holds cost for (n-1) after loop.}
// Main function for input/output and testing different approaches.int main() { int n; cout << "Enter the count of stairs : "; cin >> n;
vector<int> stairs(n); cout << "Enter the cost of stairs (space separated): "; for(int i = 0; i < n; i++) { cin >> stairs[i]; }
cout << "Costs: "; for(int cost : stairs) { cout << cost << " "; } cout << endl;
// --- Uncomment one of the following lines to test different approaches ---
// 1. Recursive (Brute Force) - Caution: Very slow for large N // int ans = min(minCostRec(stairs, n - 1), minCostRec(stairs, n - 2));
// 2. DP - Top-Down (Memoization) vector<int> dp_memo(n, 0); // Initialize dp table with 0s // Need to handle small 'n' for memoization call if n<2 if (n == 0) ans = 0; else if (n == 1) ans = stairs[0]; else ans = min(minCostMemo(stairs, n - 1, dp_memo), minCostMemo(stairs, n - 2, dp_memo));
// 3. DP - Bottom-Up (Tabulation) // int ans = minCostTab(stairs);
// 4. DP - Space Optimization // int ans = minCostSpaceOpt(stairs);
cout << "Minimum cost to reach the top: " << ans << endl;
return 0; // Indicate successful execution.}
1. Problem Statement
- Goal: Find the minimum cost to reach the top of a staircase.
- Input: An integer array
cost
wherecost[i]
is the cost ofi
-th step. - Constraint: You can either climb one or two steps at a time.
- Starting Point: You can start from either step 0 or step 1.
- Reaching the Top: The “top” is considered one step beyond the last stair (index
n
). So, if there aren
stairs, you need to reach indexn
.
2. Problem Insight
This problem is a classic Dynamic Programming problem with overlapping subproblems and optimal substructure. The key observation is that to reach a certain stair i
, you must have come from either stair i-1
or stair i-2
. The cost to reach i
would then be cost[i]
plus the minimum cost to reach i-1
or i-2
.
Crucial Point: The goal is to reach the top, which is after the last actual stair. If there are n
stairs (indexed 0
to n-1
), the “top” can be thought of as position n
. You can reach position n
either from stair n-1
(taking 1 step) or from stair n-2
(taking 2 steps). Thus, the final answer will be the minimum of dp[n-1]
and dp[n-2]
.
3. Approaches to Solving
A. Recursive Approach (Brute Force)
- Idea: Directly translate the recurrence relation.
minCost(i)
represents the minimum cost to reach stairi
. - Recurrence:
minCost(i) = cost[i] + min(minCost(i-1), minCost(i-2))
- Base Cases:
minCost(-1)
(ori < 0
):0
(cost to reach an imaginary step before the stairs, typically 0 for initialization)minCost(0)
:cost[0]
minCost(1)
:cost[1]
- Function Call in
main
:min(minCost(stairs, n-1), minCost(stairs, n-2))
because you can start from 0 or 1, and the last two steps can lead to the ‘top’. - Pros: Simple to write.
- Cons: Highly inefficient due to redundant calculations (overlapping subproblems).
- Time Complexity: $O(2^N)$ (Exponential, as it forms a recursion tree).
- Space Complexity: $O(N)$ (Due to recursion stack depth).
B. Dynamic Programming - Top-Down (Memoization)
- Idea: Optimize the recursive approach by storing the results of already computed subproblems in a
dp
table. Before computingminCost(i)
, check if it’s already in thedp
table. If yes, return the stored value; otherwise, compute and store it. - Pros: Overcomes redundant calculations of pure recursion.
- Cons: Still uses a recursion stack.
- Time Complexity: $O(N)$ (Each
minCost(i)
is computed only once). - Space Complexity: $O(N)$ (For
dp
array) + $O(N)$ (For recursion stack) = $O(N)$.
C. Dynamic Programming - Bottom-Up (Tabulation)
- Idea: Build the solution iteratively from the base cases upwards.
- Steps:
- Initialize
dp[0] = cost[0]
anddp[1] = cost[1]
. - Iterate from
i = 2
ton-1
(for stairs2
up ton-1
). - For each
i
,dp[i] = cost[i] + min(dp[i-1], dp[i-2])
.dp[i]
now stores the minimum cost to reach stairi
and pay its cost. - The final answer is
min(dp[n-1], dp[n-2])
. This is because to reach the “top” (aftern-1
stair), you can either step fromn-1
(costdp[n-1]
) or fromn-2
(costdp[n-2]
).
- Initialize
- Pros: No recursion stack overhead. Often preferred for its iterative nature and clear flow.
- Cons: Requires an array of size $N$.
- Time Complexity: $O(N)$ (Single loop).
- Space Complexity: $O(N)$ (For
dp
array).
D. Dynamic Programming - Space Optimization
- Idea: Observe that to calculate the current minimum cost to reach stair
i
, we only need the minimum costs to reach stairsi-1
andi-2
. We don’t need the entiredp
array. We can use just two variables (prev0
,prev1
) to store these previous two costs. - Steps:
- Initialize
prev0 = cost[0]
andprev1 = cost[1]
. - Iterate from
i = 2
ton-1
. - Calculate
curr = cost[i] + min(prev0, prev1)
. - Update for next iteration:
prev0 = prev1; prev1 = curr;
- The final answer is
min(prev0, prev1)
.
- Initialize
- Pros: Extremely efficient in terms of space.
- Cons: Only applicable when current state depends only on a few previous states.
- Time Complexity: $O(N)$ (Single loop).
- Space Complexity: $O(1)$ (Constant space, regardless of N).
4. Important Considerations
- Meaning of
dp[i]
: In this problem,dp[i]
usually represents the minimum cost to reach stairi
(including the cost of stairi
). - Reaching the Top: The “top” is an imaginary step after the last stair. If there are
n
stairs (0 ton-1
), the top is essentially indexn
. You can reachn
fromn-1
orn-2
. So, the minimum cost to reach the top ismin(dp[n-1], dp[n-2])
. - Edge Cases for
n
: Pay attention to smalln
values (e.g.,n=0
,n=1
,n=2
) as they are often base cases or require special handling. Your code correctly handlesn=0
by returning 0, andn=1
andn=2
directly in recursive/memoized solutions. In tabulation/space optimization,n=0
orn=1
might be handled implicitly or require anif
block at the start. Forn=0
orn=1
stairs, the loop fori=2
ton-1
won’t run, andprev0
,prev1
(ordp[0], dp[1]
) will contain the costs.