DSADynamic Programming1D Dynamic Programming

1D Dynamic Programming

Dynamic Programming

Experience is just remembered effort.

Most people feel intimidated by DP because it seems like a complex topic, but once you learn to recognize the patterns, it becomes much more approachable. The key is to understand that DP is all about figuring out the recurrence relations and then optimizing the way you compute them.

As always, try this problem for 20-30 minutes to warmup your curiosity machinery, and then read the rest of the article to see how we can solve it efficiently.

Problem: Climbing Stairs (LeetCode Link): You are climbing a staircase. It takes n steps to reach the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? 💡 Hint

The Naive Recursive Approach

The most direct way to think about this is with recursion. The number of ways to get to step n, let's call it ways(n), is the sum of the ways to get to the steps you could have come from: ways(n) = ways(n-1) + ways(n-2)

You might recognize this equation if you've seen the Fibonacci sequence before. The base cases are: ways(0) = 1 (one way to stay at the ground) and ways(1) = 1 (one way to take one step).

def climbStairs(n):
    if n <= 1:
        return 1
    return climbStairs(n - 1) + climbStairs(n - 2)

This is simple and correct, but it's incredibly slow. If you call climbStairs(5), it will calculate climbStairs(3) twice, climbStairs(2) three times, and so on. This is the problem of overlapping subproblems.

The Core of DP: Remember Your Work

Dynamic Programming is simply a technique to avoid re-solving these overlapping subproblems. We do this by storing the results of subproblems in a cache (a hash map or an array) and reusing them. There are two main ways to apply this.

Strategy 1: Memoization (Top-Down DP)

This is the most intuitive upgrade from the naive recursive solution. We keep the recursive structure but add a cache to store results. Before computing ways(n), we check if we've already calculated it. If so, we return the cached value. If not, we compute it, store it in the cache, and then return it.

def climbStairs(n: int) -> int:
    memo = {}
    def solve(i):
        if i in memo:
            return memo[i]
        if i <= 1:
            return 1
        memo[i] = solve(i - 1) + solve(i - 2)
        return memo[i]
    return solve(n)

Strategy 2: Tabulation (Bottom-Up DP)

Instead of starting from the top (n) and going down, we can start from the bottom and build our way up. We create a DP table (usually an array) and fill it out iteratively.

  1. Define DP State: dp[i] will store the number of ways to reach step i.
  2. Base Cases: dp[0] = 1 (one way to reach the start), dp[1] = 1 (one way to take one step).
  3. Recurrence Relation: dp[i] = dp[i-1] + dp[i-2].
  4. Iteration: We loop from i = 2 up to n, filling the table.
def climbStairs(n: int) -> int:
    if n <= 1: return 1
    dp = [0] * (n + 1)
    dp[0], dp[1] = 1, 1
    for i in range(2, n + 1):
        dp[i] = dp[i-1] + dp[i-2]
    return dp[n]

Space Optimization

For our climbStairs tabulation, notice that to calculate dp[i], we only need dp[i-1] and dp[i-2]. We don't need the rest of the array! This means we can optimize the space complexity from O(N)O(N) to O(1)O(1) by only storing the last two values.

def climbStairs(n: int) -> int:
    if n <= 1: return 1
    one_back, two_back = 1, 1
    for i in range(2, n + 1):
        current = one_back + two_back
        two_back = one_back
        one_back = current
    return one_back

Nice! isn't it? Can you do the same for the other problems? Try it out!

Bonus: In real-world software, we rarely compute the same expensive thing more than once. Whether it’s rendering a UI, fetching data from a remote server, or running a recommendation engine, memoization shows up as caching, and tabulation appears as precomputation.

More Problems & Variations

  1. House Robber (LeetCode Link): You are a robber planning to rob houses. You cannot rob adjacent houses. What's the maximum amount you can rob? 💡 Hint
  2. Min Cost Climbing Stairs (LeetCode Link): You are given an integer array cost where cost[i] is the cost of ith step on a staircase. Once you pay the cost, you can either climb one or two steps. Find the minimum cost to reach the top. 💡 Hint
  3. Coin Change (LeetCode Link): Given coins of different denominations and a total amount, find the fewest number of coins needed to make up that amount. 💡 Hint