Lesson 42 of 50 advanced

More DP Adventures — Coins and Stairs

Master classic DP problems that appear in contests everywhere — stairs, coins, and more!

Open interactive version (quiz + challenge)

Real-world analogy

Imagine you are at the bottom of a staircase and you can take either 1 step or 2 steps at a time. How many different ways can you reach the top? You could go 1-1-1-1, or 2-2, or 1-2-1, and so on. Instead of listing every single possibility (which gets crazy for 100 stairs!), you notice a pattern: the number of ways to reach stair N depends only on how many ways to reach stair N-1 and stair N-2. Sound familiar? It is Fibonacci in disguise! That is the beauty of DP — once you learn the patterns, you see them everywhere.

What is it?

This lesson covers classic DP problems that appear frequently in programming contests and interviews. The climbing stairs problem and coin change problem are the perfect next step after Fibonacci — they use the same DP thinking but in more practical scenarios. Mastering these patterns gives you the foundation to solve hundreds of DP variations.

Real-world relevance

The coin change algorithm is used by vending machines to figure out the best way to give you change with the fewest coins. The stairs problem is similar to path planning — like figuring out how many different routes a robot can take on a grid. Delivery companies use DP to find the cheapest shipping routes, and game designers use DP to calculate all possible move combinations.

Key points

Code example

#include <bits/stdc++.h>
using namespace std;

// ===== Problem 1: Climbing Stairs =====
// How many ways to climb N stairs (1 or 2 steps at a time)?
long long climbStairs(int n) {
    if (n <= 2) return n;
    
    vector<long long> dp(n + 1);
    dp[1] = 1;  // 1 way to climb 1 stair
    dp[2] = 2;  // 2 ways to climb 2 stairs (1+1 or 2)
    
    for (int i = 3; i <= n; i++) {
        dp[i] = dp[i - 1] + dp[i - 2];  // Come from 1 or 2 steps below
    }
    return dp[n];
}

// ===== Problem 2: Minimum Coins =====
// Find the fewest coins needed to make amount N
int minCoins(vector<int>& coins, int amount) {
    vector<int> dp(amount + 1, 1e9);  // Initialize with "impossible"
    dp[0] = 0;  // 0 coins needed for amount 0
    
    for (int i = 1; i <= amount; i++) {
        for (int c : coins) {
            if (c <= i && dp[i - c] != 1e9) {
                dp[i] = min(dp[i], dp[i - c] + 1);
            }
        }
    }
    return dp[amount] == 1e9 ? -1 : dp[amount];  // -1 if impossible
}

// ===== Problem 3: Count Ways to Make Change =====
// How many ways can you make amount N using given coins?
long long countWays(vector<int>& coins, int amount) {
    vector<long long> dp(amount + 1, 0);
    dp[0] = 1;  // 1 way to make amount 0: use no coins
    
    for (int c : coins) {             // Loop coins in outer loop!
        for (int i = c; i <= amount; i++) {
            dp[i] += dp[i - c];       // Add ways using this coin
        }
    }
    return dp[amount];
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    
    // Climbing Stairs
    int n = 10;
    cout << "Ways to climb " << n << " stairs: " << climbStairs(n) << "\n";
    
    // Minimum Coins
    vector<int> coins = {1, 5, 10, 25};
    int amount = 37;
    cout << "Min coins for " << amount << " cents: " << minCoins(coins, amount) << "\n";
    
    // Count Ways
    cout << "Ways to make " << amount << " cents: " << countWays(coins, amount) << "\n";
    
    return 0;
}

Line-by-line walkthrough

  1. 1. climbStairs(n) uses a DP array where dp[i] = number of ways to reach step i. We know dp[1] = 1 (just take one step) and dp[2] = 2 (take 1+1 or take 2). Everything else builds from these.
  2. 2. The loop for (int i = 3; i <= n; i++) fills the table bottom-up. dp[i] = dp[i-1] + dp[i-2] because you either took 1 step from stair i-1 or 2 steps from stair i-2. Same as Fibonacci!
  3. 3. minCoins() initializes dp[] with 1e9 (a huge number representing 'impossible'). Only dp[0] = 0 because you need 0 coins to make 0 cents.
  4. 4. For each amount i from 1 to N, we try every coin c. If coin c is not bigger than i and dp[i-c] is not impossible, then dp[i] = min(dp[i], dp[i-c] + 1). The +1 is for using one more coin of value c.
  5. 5. If dp[amount] is still 1e9 at the end, it means no combination of coins can make that amount, so we return -1.
  6. 6. countWays() is different — the outer loop goes over coins, not amounts! This is important. Looping coins outside prevents counting the same combination in different orders (like 1+5 and 5+1 as separate ways).
  7. 7. For each coin c, we update dp[i] += dp[i-c] for all valid amounts. This means: the number of ways to make amount i increases by the number of ways to make (i minus this coin).
  8. 8. dp[0] = 1 in countWays because there is exactly 1 way to make amount 0: use no coins at all. This base case is crucial — without it, all values would stay 0.
  9. 9. In main(), we test all three functions. Climbing 10 stairs gives 89 ways. Making 37 cents with {1,5,10,25} takes 4 coins (25+10+1+1). These are classic results you should memorize!

Spot the bug

#include <bits/stdc++.h>
using namespace std;

int minCoins(vector<int>& coins, int amount) {
    vector<int> dp(amount + 1, 1e9);
    dp[0] = 0;
    
    for (int i = 1; i <= amount; i++) {
        for (int c : coins) {
            if (c <= i) {
                dp[i] = min(dp[i], dp[i - c] + 1);
            }
        }
    }
    return dp[amount];
}

int main() {
    vector<int> coins = {3, 7};
    cout << minCoins(coins, 5) << "\n";  // Should say impossible!
    return 0;
}
Need a hint?
What happens when it is impossible to make the amount? What value does the function return? Is there a missing check?
Show answer
The bug is that the function does not check if dp[amount] is still 1e9 (impossible). When you try to make 5 cents with coins {3, 7}, it is impossible — but the function returns 1000000000 instead of -1. The fix is to add a check at the end: return dp[amount] >= 1e9 ? -1 : dp[amount]. Also, there is a subtle issue: dp[i-c] + 1 could overflow if dp[i-c] is 1e9. Add a check: if (c <= i && dp[i-c] != (int)1e9) before updating.

Explain like I'm 5

Imagine you want to get to the top of a playground slide that has 5 steps. You can go up 1 step or jump up 2 steps at a time. How many different ways can you get to the top? You could go 1-1-1-1-1, or 2-1-1-1, or 1-2-2... there are lots of ways! Instead of listing them all (which is hard), we use a trick: the number of ways to reach step 5 is just the ways to reach step 4 plus the ways to reach step 3. Because from step 4 you take 1 step, and from step 3 you jump 2 steps. Easy peasy!

Fun fact

The coin change problem is so important that it appears in almost every programming interview at big tech companies. Google, Amazon, and Microsoft have all asked variations of it! The problem was first studied by mathematicians over 200 years ago, long before computers existed. Euler and other famous mathematicians worked on counting coin combinations. Now you are learning the same math that fascinated geniuses!

Hands-on challenge

Solve these three problems step by step. First, modify the climbing stairs solution to allow 1, 2, or 3 steps — how many ways to climb 10 stairs now? Second, find the minimum coins to make 63 cents using coins {1, 5, 10, 25}. Third, count how many ways you can make 50 cents using {1, 5, 10, 25}. Bonus: solve the House Robber problem — given an array of house values, find the maximum you can rob without robbing two adjacent houses. Hint: dp[i] = max(dp[i-1], dp[i-2] + arr[i]).

More resources

Open interactive version (quiz + challenge) ← Back to course: CP Zero to Hero