Dynamic programming · Data Structures and Algorithms
How do you stop solving the same small problem over and over?
Course contentsWhy you need it
Some recursive solutions repeat the same calls millions of times. Dynamic programming (DP) saves each small answer once and reuses it, turning exponential time into linear. It's behind route planners, spell checkers and many of the harder interview questions.
The intuition
Climbing stairs taking 1 or 2 steps at a time: the ways to reach step n = ways(n − 1) + ways(n − 2). Plain recursion recomputes ways(3) again and again; the call count roughly doubles with each step.
Two fixes:
- Memoisation (top-down): keep the recursion, but store answers in a dict. Python's
@functools.lru_cachedoes it for you. - Tabulation (bottom-up): fill a list from the smallest case up:
ways[i] = ways[i-1] + ways[i-2].
The hard part is the first step: define what one small answer means ("ways[i] = number of ways to reach step i"), and how it's built from smaller ones.
Definition
Dynamic programming
Solving a problem by combining answers to overlapping subproblems, computing each subproblem once.
Memoisation
Caching a function's results by its arguments, so repeated calls return instantly.
Examples
Count the repeated work
Try it yourselfPlain recursion versus memoised recursion, counting calls.
calls = 0
def ways(n):
global calls
calls += 1
if n <= 1:
return 1
return ways(n - 1) + ways(n - 2)
print(ways(20), calls)
memo = {}
calls = 0
def ways_memo(n):
global calls
calls += 1
if n <= 1:
return 1
if n not in memo:
memo[n] = ways_memo(n - 1) + ways_memo(n - 2)
return memo[n]
print(ways_memo(20), calls)- L8Two calls per call: the count explodes.
- L1010946 ways, 21891 calls.
10946 21891 - L20Each n is computed once.
- L24Same answer, 39 calls.
10946 39
Fewest coins, bottom-up
Try it yourselfbest[a] is the fewest coins that make amount a. Fill it from 0 upward.
def fewest_coins(coins, amount):
INF = amount + 1
best = [0] + [INF] * amount
for a in range(1, amount + 1):
for c in coins:
if c <= a and best[a - c] + 1 < best[a]:
best[a] = best[a - c] + 1
return best[amount] if best[amount] != INF else -1
print(fewest_coins([1, 3, 4], 6))
print(fewest_coins([5, 10], 3))- L30 coins make amount 0. INF means "not possible yet".
- L6Use coin c on top of the best way to make a − c.
- L103 + 3: two coins. (Greedy 4 + 1 + 1 would use three.)
2 - L11No mix of 5 and 10 makes 3.
-1
Common mistakes
Greedy instead of DP
Always taking the biggest coin fails for coins [1, 3, 4] and amount 6. When a choice affects later choices, check DP.
A vague state
If you can't say in one sentence what best[i] means, the recurrence will be wrong. Write the sentence first.
Quick quiz
1. What does this print?
ways = [1, 1]
for i in range(2, 6):
ways.append(ways[i - 1] + ways[i - 2])
print(ways)Show the answer
Answer: A
Each entry is the sum of the two before it.
2. What makes a problem a good fit for dynamic programming?
Show the answer
Answer: A
Overlapping subproblems plus optimal substructure: that's when caching pays off.
Practice
3 exercises in three steps: predict what a program prints, change a program to meet a new requirement, then write one from scratch. Your code runs in the browser and is checked the moment you submit.
- PredictFill the table
Type exactly what this program prints.
- ModifyMemoise it
grid_paths(r, c)counts the paths from the top-left to the bottom-right of an r × c grid, moving only right or down. It's correct but exponential. Add a memo dict (orfunctools.lru_cache) so the speed test passes. - WriteFewest coins
Write
fewest_coins(coins, amount)that returns the fewest coins that add up toamount, or -1 if it can't be done. Build a tablebestfrom 0 up to amount.
Practise this lesson
Open the interactive lesson to edit and run these examples and solve the exercises with instant feedback. Free to start, no sign-up.
Interactive lessons open in order. If you have not done the earlier ones yet, you start at the course map.
Terms in this lesson
| Term | Chinese | Definition |
|---|---|---|
| Dynamic programming | 动态规划 | Reusing answers to overlapping subproblems instead of recomputing them. |
| Memoisation | 记忆化 | Caching a function's results by its arguments. |