DSA Notes
Master dynamic programming from scratch — what DP really is, the two essential properties (overlapping subproblems and optimal substructure), how to identify a DP problem, the four-step DP recipe, comparison with divide-and-conquer and greedy, and a taxonomy of DP problem types.
What Is Dynamic Programming?
Dynamic Programming (DP) is an optimization technique that solves complex problems by breaking them into simpler overlapping subproblems, solving each subproblem only once, and storing the result to avoid redundant computation.
The name is somewhat misleading — it has nothing to do with "dynamic" in the programming sense. Richard Bellman coined the term in the 1950s, partly because "dynamic" sounded impressive and would secure funding from the US Department of Defense (which was paying for his research). The "programming" refers to planning or scheduling, not computer programming.
At its core, DP is about a single powerful idea: "If you've already solved a subproblem, don't solve it again."
DP vs Divide-and-Conquer
Both break problems into subproblems and combine solutions. The key difference:
| Divide-and-Conquer | Dynamic Programming | |
|---|---|---|
| Subproblems | Independent | Overlapping |
| Storage needed | No (each solved once) | Yes (cache results) |
| Classic examples | Merge Sort, Binary Search | Fibonacci, Knapsack |
| When to use | Independent partitions | Repeated subproblems |
Merge Sort divides an array into two halves — the left half and right half don't share elements. They're computed once each and combined. No overlap, no memoization needed.
Fibonacci needs fib(3) to compute both fib(4) and fib(5). Without storing fib(3), it gets recomputed exponentially many times.
DP vs Greedy
Both solve optimization problems. The difference is in how they make decisions:
| Greedy | Dynamic Programming | |
|---|---|---|
| Decision making | One locally-optimal choice at a time | Consider all choices, pick best overall |
| Revisit decisions | No — irrevocable | No revisits needed (but evaluates all) |
| Correctness guarantee | Only if greedy-choice property holds | Always correct if substructure holds |
| Speed | Usually faster | Usually slower but more general |
| Example | Activity selection, Huffman | 0/1 Knapsack, Edit Distance |
When to use greedy: If a locally optimal choice always leads to a globally optimal solution (greedy-choice property proven).
When to use DP: When locally optimal choices can lead to globally suboptimal results.
| Coin change | denominations = [1, 5, 6], target = 10 |
| Greedy: Take 6 first | remaining 4 → take 1, 1, 1, 1 = 5 coins |
| DP | Take 5 + 5 = 2 coins ← OPTIMAL |
The Four-Step DP Recipe
Every DP solution follows the same design process:
Step 1 — Define the State
A state completely describes a subproblem. The state tells you: "Given this information, what is the answer?"
| Fibonacci | state = the number n |
| 0/1 Knapsack | state = (item index, remaining capacity) |
| Longest Common Subsequence | state = (position in s1, position in s2) |
| Climbing Stairs | state = current step number |
Step 2 — Define the Recurrence (Transition)
How do smaller states combine to form the answer for a larger state?
Fibonacci
dp[n] = dp[n-1] + dp[n-2]
0/1 Knapsack (item i has weight w_i, value v_i):
dp[i][w] = max(
dp[i-1][w], # Don't take item i
dp[i-1][w - w_i] + v_i # Take item i (if w >= w_i)
)
LCS
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])
Step 3 — Define the Base Cases
What are the trivially known values that don't depend on any other state?
| Fibonacci | dp[0] = 0, dp[1] = 1 |
| Knapsack: dp[0][w] = 0 (no items | value 0) |
| dp[i][0] = 0 (capacity 0 | value 0) |
| LCS | dp[i][0] = 0 (empty s2) |
Step 4 — Determine the Final Answer
Which state contains the answer to the original problem?
| Fibonacci | dp[n] |
| Knapsack | dp[n][W] (n items, capacity W) |
| LCS | dp[m][n] (lengths of both strings) |
Two Approaches: Memoization vs Tabulation
Memoization (Top-Down DP)
Start with the original problem, recurse toward base cases, and cache results.
Pros of memoization:
- Natural to implement (just add cache to recursion)
- Only computes states that are actually needed
- Handles irregular state spaces
Cons:
- Recursion overhead
- Risk of stack overflow for large n
Tabulation (Bottom-Up DP)
Build the solution table starting from base cases and work toward the final answer.
Pros of tabulation:
- No recursion overhead
- No stack overflow risk
- Often more space-efficient (sliding window over table)
Identifying DP Problems
These signals in a problem statement strongly suggest DP:
Signal 1 — "Count the number of ways"
Signal 2 — "Find the minimum/maximum"
Signal 3 — "Is it possible to..."
Signal 4 — Optimal choices depend on future consequences
DP Problem Taxonomy
| Category | Classic Problems |
|---|---|
| 1D Linear DP | Fibonacci, Climbing Stairs, House Robber, Jump Game |
| 2D Grid DP | Unique Paths, Minimum Path Sum, Dungeon Game |
| String DP | LCS, LIS, Edit Distance, Palindrome Partitioning |
| Knapsack variants | 0/1 Knapsack, Unbounded Knapsack, Subset Sum |
| Interval DP | Matrix Chain Multiplication, Burst Balloons, Palindrome DP |
| Tree DP | House Robber III, Max Path Sum in Tree |
| Bitmask DP | Traveling Salesman, Assignment Problem |
| Digit DP | Count numbers with property in range [a, b] |
Simple First Example — Climbing Stairs
This is structurally identical to Fibonacci — a perfect first DP problem.
Summary
Dynamic programming is the technique of caching solutions to overlapping subproblems to avoid recomputation:
Two required properties:
- Overlapping subproblems — same subproblem computed multiple times
- Optimal substructure — optimal solution built from optimal sub-solutions
The four-step recipe:
- Define state — what information describes a subproblem?
- Define recurrence — how do smaller states combine?
- Define base cases — what trivial states do you know?
- Find the answer — which state is the final answer?
Two implementation styles:
- Memoization (top-down): recursion + cache
- Tabulation (bottom-up): iteration, build table from base cases
DP feels difficult at first because identifying the right state definition is non-obvious. With practice, pattern recognition develops quickly. The next lessons cover memoization and tabulation in depth, then build through increasingly complex DP patterns.
*Next Lesson: Memoization*
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Dynamic Programming — Complete Introduction with Properties and Problem Identification.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Data Structures & Algorithms topic.
Search Terms
data-structures-algorithms, data structures & algorithms, data, structures, algorithms, dynamic, programming, introduction
Related Data Structures & Algorithms Topics