Recommended Free Tools
Dynamic programming (DP) solves a problem by breaking it into smaller questions, answering each smaller question once, and reusing that answer wherever it is needed again. The method works only when two conditions hold: the same smaller questions recur, and the answer to the full problem can be built from answers to those smaller questions. Most of the difficulty lies in choosing the smaller questions correctly, so that is where this article spends most of its time.
What a DP state actually is
Every DP solution rests on a state: a precisely defined smaller question, described by its parameters. A state is not a vague idea such as “the best answer so far.” It is a statement like “the minimum number of coins needed to make exactly n cents, using only the first k coin types.” Each parameter has a fixed meaning, and the state’s answer is a single well-defined value.
MIT’s introductory treatment of the topic in 6.006 (Spring 2020, Lecture 16) starts from this same discipline: define the state in words and by its parameters before writing anything else. The reason is practical. If the state leaves out information that affects the answer, the recurrence built on it will either give wrong results or require extra information that the table does not hold.
The recurrence connects states
A recurrence expresses the answer to one state in terms of the answers to smaller states. To write one, ask what the final step, or the final choice, could be for that state. Each possible choice points to a smaller state, and the recurrence takes the best (or the count, or the logical OR) across those choices.
#1 Best Overall
- Used Book in Good Condition
The recurrence has to carry enough information. Suppose a state asks only for the length of the best path, but the choice at the next step depends on which vertex was last used. Then the state must include that vertex. This is why the definition and the recurrence must be designed together rather than in sequence.
The two properties that make DP possible
DP needs two properties. Neither is a formula that guarantees success; each is a condition you must check for the specific problem.
Overlapping subproblems
Overlap means that a naive recursive solution reaches the same state along more than one path. Storing the answer the first time it is computed, and looking it up thereafter, removes the repeated work. This storing step is called memoization when it is attached to a recursive function, and it is the core of the top-down approach described below.
Overlap is what separates DP from divide-and-conquer. Merge sort is the standard boundary case. It has optimal substructure in the ordinary sense: if you sort the two halves correctly and merge them, you have sorted the whole list. But its recursive calls work on disjoint sublists, so no sublist is ever solved twice. MIT’s 6.00SC lecture (Spring 2011, Lecture 23) uses this example to show that optimal substructure alone does not create the reuse that motivates DP.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsOptimal substructure
MIT’s 6.046J lecture notes (Spring 2012, Lecture 6) state the requirement directly:
“The key feature that a problem must have in order to be amenable to dynamic programming is that of optimal substructure: the optimal solution to the problem must contain optimal solutions to subproblems.”
In practice this means that the optimal answer for the full problem can be assembled from optimal answers for smaller states. It is not enough that a solution decomposes; the pieces must be the right pieces. If the best overall choice depends on a piece that is not optimal in isolation, the decomposition fails.
A worked example: fewest coins
Take a coin system of 1, 3, and 4 cents, and ask for the fewest coins that make a given amount n. Define the state as f(n): the fewest coins that sum exactly to n, using any of the three coin types. The base case is f(0) = 0. For n ≥ 1, the last coin used must be one of the denominations c with c ≤ n, so:
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Rank #3
f(n) = 1 + min over coins c ≤ n of f(n − c)
Computing bottom-up gives this table:
| n | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| f(n) | 0 | 1 | 2 | 1 | 1 | 2 | 2 |
For n = 6, the three candidates are f(5) + 1 = 3, f(3) + 1 = 2, and f(2) + 1 = 3. The minimum is 2, achieved by choosing the 3-cent coin, which leaves 3 cents, which is one coin. The solution is 3 + 3.
Two details matter here. The state f(n) does not need to remember which coins were used, because the recurrence only needs the count for smaller amounts. Also, greedy choice fails on this same system: taking the largest coin first gives 4 + 1 + 1 for 6 cents, three coins instead of two. Greedy is therefore not a shortcut to the DP answer; its correctness must be proven separately.
Top-down or bottom-up evaluation
MIT 6.006 (Spring 2020, Lecture 15 notes) presents two evaluation styles that compute the same table in different orders.
| Aspect | Top-down (memoized recursion) | Bottom-up (iterative table) |
|---|---|---|
| How it starts | From the original problem, recursing toward base cases | From base cases, filling states in dependency order |
| Which states are computed | Only those reachable from the original problem | Every state in the table, whether needed or not |
| Ordering | Implicit, set by the recursion | Must be made explicit and valid (for example, increasing n) |
| Main risk | Recursion depth can exceed stack limits on large inputs | Computing states that the answer never uses |
Both approaches compute the same values. Choose top-down when the reachable states are a small fraction of the table and the recursion depth is safe. Choose bottom-up when you want predictable memory use and an explicit order.
Dependency order and acyclicity
Bottom-up evaluation is only correct if every state is computed after the states it depends on. MIT 6.006 frames this as showing that the dependencies form an acyclic directed graph. If state A needs state B and state B needs state A, no order exists and the recurrence is not well-founded. In the coin example, f(n) depends only on smaller amounts, so increasing n is a valid order. A recurrence that refers to a state of the same size, or to a larger one, needs a different formulation.
Reconstructing the actual answer
A DP table often gives only the optimal value. When the task asks for the path, the subsequence, or the set of coins, store a predecessor choice alongside each value. In the coin example, record the coin c that achieved the minimum for each n. For 6 cents, the stored choice is 3. Following the chain 6 → 3 → 0 recovers {3, 3}. Many DP solutions fail in exam and production settings not because the recurrence is wrong but because this step is omitted.
Counting the work
MIT 6.006 analyzes DP by summing the work over all states. If there are S states and each costs at most O(W) work, the total is bounded by O(S · W). This is the central idea of complexity analysis for DP, and it has two consequences.
- The benefit of reuse disappears if the state count is too large. A state space that grows exponentially in the input size gives no useful bound.
- Per-state work matters as much as the count. A cheap state definition can still be slow if each transition scans a long list.
The coin example has n + 1 states, and each state tries k coin types, so the work is O(n·k). This is not automatically polynomial. The amount n is a numeric value, and its binary representation takes only about log n bits. A bound that grows with n itself is therefore pseudopolynomial: efficient when the numbers are small, exponential in the number of bits when they are large. MIT’s 6.006 course index (Spring 2008) lists knapsack and pseudopolynomial time together for exactly this reason.
Best Value
How DP differs from greedy and divide-and-conquer
| Approach | Subproblem structure | How solutions combine | What must be proven |
|---|---|---|---|
| Dynamic programming | Overlapping states, reused | Recurrence picks the best or combined value over choices | Optimal substructure and a valid dependency order |
| Divide-and-conquer | Generally disjoint pieces, such as merge sort halves | Solutions to pieces are merged directly | That the split and merge produce a correct whole |
| Greedy | A sequence of local choices | Each choice is committed and not revisited | A separate exchange or greedy-choice argument; optimal substructure alone is not enough |
MIT’s 6.046J notes describe the greedy contrast in terms of how inner solutions affect the way they are extended, which is why a problem with optimal substructure can still need a different method.
A diagnostic checklist before you write any code
- Can you state one table entry in a sentence, including every parameter and what the base case means?
- Does a naive recursion reach the same state along two or more paths?
- Does the optimal answer for the full problem contain optimal answers for the states it uses?
- Does every recurrence step refer to a strictly smaller state under some well-founded order?
- Does the state carry all information the next choice depends on?
- Is the number of states times the work per state acceptable for the actual input sizes, including whether numeric values inflate the state range?
- If the task asks for an object rather than a number, is a predecessor choice stored for each state?
Common failure modes
- Vague state. “Best solution for the first i items” does not say whether the capacity or the last item matters. Add the parameters the recurrence needs.
- Missing information. A recurrence that works for the total but not for the restricted case usually lacks a parameter such as the last choice made.
- Cyclic dependencies. If bottom-up order cannot be found, the states are wrong, or the problem needs a graph algorithm rather than a simple table.
- Silent state explosion. A state that tracks every subset of items grows exponentially. Look for a smaller parameterization before optimizing the code.
- Assuming greedy. If a local choice seems to work on a few examples, test it against the counterexample method used above before relying on it.
Further reading
MIT OpenCourseWare’s 6.006 and 6.046J lecture materials cover the state, recurrence, and analysis steps described here in full. The 6.046J notes name Introduction to Algorithms by Cormen, Leiserson, Rivest, and Stein (CLRS) as supplemental reading. Check the current edition before purchasing, and treat it as a reference after you have worked through a few state definitions yourself.
The Bottom Line
A dynamic programming solution is only as good as its state. Define a precise smaller question, confirm that the optimal answer is built from optimal answers to those questions, order the computation so dependencies come first, and count states times work before trusting the bound.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




