Introduction to Dynamic Programming
This lesson defines dynamic programming (DP) as an optimization technique, explains optimal substructure and overlapping subproblems, contrasts DP with greedy and divide-and-conquer, compares memoization vs tabulation, and maps classic DP problems to real-world applications.
What is Dynamic Programming?
Dynamic programming is a method for solving complex problems by breaking them into simpler overlapping subproblems, storing the results of those subproblems, and reusing them instead of recomputing. The word dynamic refers to the table of stored answers built over time—not to runtime speed by itself.
DP is not a single algorithm like binary search; it is a problem-solving paradigm. You define a recurrence (how a larger answer depends on smaller ones), identify a state (what you must remember), and fill or cache a table of optimal values.
Schematic: Fibonacci with overlap
Naive recursion recomputes the same values many times. DP stores each fib(k) once:
fib(5) ├── fib(4) fib(3) ← fib(3) computed again without DP │ ├── fib(3) fib(2) │ │ fib(2) fib(1) │ └── fib(2) fib(1) └── fib(3) ← duplicate subtree With DP: fib(0)…fib(5) stored once → O(n) time
The Two DP Properties
A problem is a strong DP candidate when both properties hold:
1. Optimal Substructure
An optimal solution to the full problem can be built from optimal solutions to its subproblems.
Example:
- Shortest path in a graph
- Best way to pack a knapsack of capacity
Wuses the best answers for smaller capacities - Minimum cost to reach position
idepends on minimum cost to reachi-1andi-2
2. Overlapping Subproblems
The same subproblem appears many times in the recursion tree.
Examples:
- Fibonacci numbers
- Edit distance
- Coin change
- Longest common subsequence
DP vs Greedy vs Divide-and-Conquer
All three decompose problems, but the guarantees and storage differ:
| Approach | Idea | Stores sub-answers? | Typical signal |
|---|---|---|---|
| Divide & Conquer | Split into independent parts, combine | Usually no table | Merge sort, quicksort partitions |
| Greedy | One locally best choice per step | Often O(1) extra | Proof that greedy choice is safe (activity selection) |
| Dynamic Programming | Try/recur on subproblems; reuse cached optima | Table or memo map | “Count ways”, “min/max over choices”, overlapping states |
Quick Comparison Examples
Divide & Conquer Example:
Merge Sort: [5,2,4,6,1,3] → Split into [5,2,4] and [6,1,3] → Split further until single elements → Merge back (no overlapping subproblems)
Greedy Example:
Coin Change (US coins): 30 cents → Always take largest coin: 25 + 5 = 30 ✓ → No need to try all combinations
DP Example:
Coin Change (any coins): 30 cents with [1,3,4] → Need to try combinations: 4+4+4+4+4+4+4+2 = 8 coins 4+4+4+4+4+3+3+3+1 = 9 coins → Must consider all possibilities
Memoization vs Tabulation
Top-Down (Memoization)
Write the natural recursive solution, then cache results in a hash map or array when a state is first computed.
Pros
- Easy to start from the problem statement
- Only computes needed states
- Natural recursive thinking
Cons
- Recursion overhead
- Risk of stack overflow for large problems
- Extra space for recursion stack
Example — Fibonacci:
def fib_memo(n, memo={}):
if n <= 1:
return n
if n not in memo:
memo[n] = fib_memo(n-1, memo) + fib_memo(n-2, memo)
return memo[n]
Bottom-Up (Tabulation)
Fill a table iteratively from base cases upward, often controlling memory by keeping only the previous row or two.
Pros
- No recursion overhead
- Better space optimization possible
- Avoids stack overflow
Cons
- Must determine correct iteration order
- May compute unnecessary states
Example — Fibonacci:
def fib_tab(n):
if n <= 1:
return n
dp = [0] * (n + 1)
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
Which One to Choose?
| Factor | Memoization | Tabulation |
|---|---|---|
| Ease of implementation | Easier | Harder |
| Space | Usually more | Usually less |
| Time | Sometimes faster (only needed states) | Consistent |
| Recursion depth | Risk of stack overflow | No recursion |
| Debugging | Harder (recursion) | Easier (iterative) |
| When to use | Quick solution, small input | Large input, optimal space |
Both compute the same DP table in spirit; choose based on clarity, iteration order, and space constraints.
Where DP Shows Up
DP models optimization and counting over structured choices:
Classic DP Problems
1. Grid Problems
- Unique paths
- Minimum path sum
- Dungeon game
- Cherry pickup
2. Knapsack & Coin Problems
- 0/1 Knapsack
- Coin change (minimum coins)
- Coin change (number of ways)
- Partition equal subset sum
3. String Problems
- Longest common subsequence
- Edit distance
- Word break
- Palindrome partitioning
- Wildcard matching
4. Sequence Problems
- Longest increasing subsequence
- Maximum subarray (Kadane's algorithm)
- Decode ways
5. Stock Problems
- Best time to buy/sell stock (I, II, III, IV)
- With cooldown
- With transaction fee
6. Miscellaneous
- House robber (I, II, III)
- Jump game
- Interleaving strings
- Distinct subsequences
Real-Life Applications
Dynamic programming is not only an interview topic—it is the backbone of optimization in science, engineering, and everyday software. The table below maps classic DP families to where they appear in the real world.
| DP family | Real-world use | Example |
|---|---|---|
| Grid / paths | Robot navigation, warehouse picking, game maps | Count paths in a factory floor grid avoiding obstacles |
| Knapsack / subset | Resource allocation, cargo loading, portfolio selection | Maximize value of packages that fit in a truck |
| Coin change | POS change-making, vending machines, fare systems | Fewest coins to return $2.37 in change |
| String DP | Spell check, plagiarism detection, DNA alignment | Edit distance between “kitten” and “sitting” |
| Sequence DP | Stock trading rules, trend analysis, signal processing | Best buy/sell days with transaction limits |
| Interval DP | Matrix chain multiplication, query optimization | Optimal order to multiply matrices in graphics pipelines |
| Tree DP | Network routing on trees, org-chart decisions | Maximum independent set on a dependency tree |
| Game theory | Chess engines, auction bidding, adversarial AI | Minimax with memoized game states |
Cross-Domain Examples
- Bioinformatics: Align two DNA sequences to find similarity (edit distance / LCS variants)
- Natural language processing: Word break for Chinese/Japanese; autocorrect via edit distance
- Operations research: Scheduling jobs, assigning tasks under budgets (knapsack family)
- Computer graphics: Seam carving and image segmentation use DP on grids
- Economics: Multi-period investment and consumption (stock DP models)
- Telecom: Error-correcting codes and Viterbi decoding in cell networks
Key Takeaway
Dynamic programming = recurrence + overlapping subproblems + stored optimal sub-answers.
Steps to Solve DP Problems
- Identify DP properties — Check for optimal substructure and overlapping subproblems.
- Define state — What information is needed to make decisions? What parameters identify a subproblem?
- Define recurrence — How does the optimal solution depend on smaller subproblems?
- Define base cases — What are the simplest subproblems and their answers?
- Choose implementation — Top-down (memoization) or bottom-up (tabulation); consider time and space constraints.
- Optimize — Can we reduce space? Can we improve time complexity?
- Trace and verify — Test with small inputs; verify the recurrence works correctly.
Next Steps
Continue Learning
- Memoization vs Tabulation — Deep dive into both approaches with examples
- DP Properties & Complexity — Detailed analysis of optimal substructure and overlapping subproblems
- Fibonacci & Climbing Stairs — Beginner problems to apply DP
Practice Problems
Beginner Level:
Intermediate Level:
Interview Preparation
- Dynamic Programming Patterns — Recognize common DP patterns
- Code Templates — Reusable DP templates
- Common Mistakes — Avoid typical DP errors
Summary
| Concept | Key Points |
|---|---|
| Definition | Optimizing overlapping subproblems with stored results |
| Properties | Optimal substructure + Overlapping subproblems |
| Approaches | Top-down (memoization) vs Bottom-up (tabulation) |
| Versus | DP ≠ Greedy ≠ Divide-and-Conquer |
| Applications | Count ways, min/max, decision problems, string matching |
| Goal | Time: Exponential → Polynomial; Space: Usually O(n) or O(n²) |
Quick Reference Card
When to use DP:
- ✓ Problem asks for “number of ways”
- ✓ Problem asks for “minimum/maximum”
- ✓ Problem has multiple overlapping subproblems
- ✓ Recursive solution is exponential
When NOT to use DP:
- ✗ No overlapping subproblems
- ✗ Greedy works
- ✗ Simple recursion without state repetition
Common Time Complexity Classes:
- O(n) — 1D DP (Fibonacci, House Robber)
- O(n²) — 2D DP (LCS, Edit Distance)
- O(n × W) — Knapsack (W = capacity)
- O(n × 2ⁿ) — Bitmask DP