Overlapping subproblems + optimal substructure. Cache computed results. Bottom-up fills a table; top-down memoizes recursion.
Coins: [1, 3, 4]. Find minimum coins for each amount 0→8. Each cell = min(dp[amt-coin]+1) for each valid coin.
| Pattern | State | Key Problems |
|---|---|---|
| 1D Linear | dp[i] | Fibonacci, Climbing Stairs, House Robber |
| 2D Grid | dp[i][j] | Unique Paths, Minimum Path Sum |
| String/Sequence | dp[i][j] = substr | LCS, Edit Distance, LPS |
| Knapsack | dp[i][w] = value | 0/1 Knapsack, Partition Equal Subset |
| Interval | dp[i][j] = range | Burst Balloons, Matrix Chain |
| State Machine | dp[state] | Best Time to Buy/Sell Stock |
| Top-Down (Memo) | Bottom-Up (Tabulation) | |
|---|---|---|
| Approach | Recursion + cache | Iteration + table fill |
| Space | O(n) call stack | O(n) or O(1) optimized |
| Intuitive? | Easier to write first | Harder to derive |
| Speed | Slightly slower | Faster (no recursion overhead) |
# Top-down: recursion + @lru_cache from functools import lru_cache @lru_cache(maxsize=None) def climb(n): if n <= 2: return n return climb(n-1) + climb(n-2) # Bottom-up: fill table iteratively def climb_bu(n): if n <= 2: return n dp = [0] * (n + 1) dp[1], dp[2] = 1, 2 for i in range(3, n + 1): dp[i] = dp[i-1] + dp[i-2] return dp[n] # Space-optimized: O(1) def climb_opt(n): a, b = 1, 2 for _ in range(2, n): a, b = b, a + b return b
# Coin Change — min coins def coin_change(coins, amount): dp = [float('inf')] * (amount + 1) dp[0] = 0 for amt in range(1, amount + 1): for coin in coins: if coin <= amt: dp[amt] = min(dp[amt], dp[amt - coin] + 1) return dp[amount] if dp[amount] != float('inf') else -1 # House Robber def rob(nums): prev2, prev1 = 0, 0 for n in nums: curr = max(prev1, prev2 + n) prev2, prev1 = prev1, curr return prev1 # Longest Common Subsequence def lcs(s1, s2): m, n = len(s1), len(s2) dp = [[0]*(n+1) for _ in range(m+1)] for i in range(1, m+1): for j in range(1, n+1): if s1[i-1] == s2[j-1]: dp[i][j] = dp[i-1][j-1] + 1 else: dp[i][j] = max(dp[i-1][j], dp[i][j-1]) return dp[m][n]