The Core Idea
Dynamic Programming (DP) = recursion + memoization. It solves problems by:
-
Breaking them into overlapping subproblems (same subproblems repeat)
-
Solving each subproblem ONCE and caching the result
-
Building up to the final answer
Two Approaches
Top-Down (Memoization)
Start with the original problem, recurse into subproblems, cache results.
from functools import lru_cache
@lru_cache(maxsize=None) # Python's built-in memoization decorator
def fib(n):
if n <= 1:
return n
return fib(n - 1) + fib(n - 2)
Bottom-Up (Tabulation)
Start with the smallest subproblems, build up to the answer using a table.
def fib(n):
if n <= 1:
return n
dp = [0] * (n + 1) # Table to store subproblem results
dp[0], dp[1] = 0, 1
for i in range(2, n + 1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
When Does DP Apply?
Two conditions:
-
Optimal substructure: Optimal solution uses optimal solutions to subproblems
-
Overlapping subproblems: Same subproblems are solved multiple times
The DP Problem-Solving Framework
-
Define the state: What does
dp[i]represent? -
Find the recurrence: How does
dp[i]relate to smaller subproblems? -
Set base cases: What are the trivial answers?
-
Choose direction: Top-down or bottom-up?
-
Optimize space: Can you reduce from O(n) to O(1)?
Exercises
-
Climbing Stairs — The simplest DP problem
-
Coin Change — Classic DP with optimization
-
Longest Common Subsequence — 2D DP