What are the two key attributes of a DP problem?
Updated Apr 28, 2026
Short answer
The two key attributes of a Dynamic Programming problem are overlapping subproblems and optimal substructure. Overlapping subproblems mean the same smaller problems are solved multiple times, while optimal substructure means the optimal solution can be built from optimal solutions of smaller subproblems. When both properties exist, Dynamic Programming can avoid repeated work and efficiently solve the problem.
Deep explanation
Dynamic Programming (DP) is a technique for solving complex problems by breaking them into smaller subproblems, solving those subproblems once, and storing their results for reuse.
A problem is a good candidate for Dynamic Programming when it has these two important properties:
- Overlapping Subproblems
- Optimal Substructure
Both are necessary because they solve different parts of the DP process.
---
1. Overlapping Subproblems
A problem has overlapping subproblems when the same smaller problems are solved repeatedly during the computation.
Instead of recalculating the same answer multiple times, DP stores the result and reuses it.
Example: Fibonacci
The Fibonacci recurrence is:
F(n) = F(n - 1) + F(n - 2)A simple recursive implementation:
def fibonacci(n): if n <= 1: return n
return fibonacci(n - 1) + fibonacci(n - 2)Calling:
fibonacci(5)creates a recursion tree:
F(5) / \ F(4) F(3) / \ / \ F(3) F(2) F(2) F(1)Notice that:
F(3)F(2)are calculated more than once.
These repeated calculations are overlapping subproblems.
DP solves this by storing results:
memo[3] = 2memo[2] = 1When the same value is needed again, it is retrieved instead of recomputed.
---
2. Optimal Substructure
A problem has optimal substructure when the optimal solution to the entire problem can be created using optimal solutions to smaller subproblems.
In simpler terms:
The best answer for a large problem contains the best answers for smaller versions of that problem.
Example: Shortest Path
Suppose the shortest route from A to C is:
A → B → CFor this route to be optimal:
- The path from A to B must be the shortest path from A to B.
- The path from B to C must be the shortest path from B to C.
If a shorter A → B route existed, we could replace that section and create an even shorter A → C route.
Therefore, the solution contains optimal solutions to smaller problems.
---
How the Two Properties Work Together
Consider the Coin Change problem:
Find the minimum number of coins needed to make a given amount.
For example:
Coins: [1, 2, 5]Amount: 5The answer is:
5 = 5using one coin.
To solve larger amounts, we solve smaller amounts:
Best(5)= 1 + Best(4)= 1 + Best(3)= ...This works because:
Optimal Substructure
The best way to make amount 5 depends on the best ways to make smaller amounts.
Overlapping Subproblems
The same amounts are calculated repeatedly:
Best(3)Best(2)Best(1)DP stores these answers and avoids recalculating them.
---
Dynamic Programming Approaches
Once a problem has both properties, we can implement DP in two common ways.
Memoization (Top-Down)
Uses recursion and stores results:
def solve(n, memo={}): if n in memo: return memo[n]
# calculate result
memo[n] = result return resultAdvantages:
- Natural extension of recursive solutions.
- Only computes required states.
Disadvantages:
- Uses recursion stack.
- Can cause stack overflow for very deep problems.
---
Tabulation (Bottom-Up)
Uses iteration and builds solutions from smaller states:
dp[0] = base_case
for i in range(1, n + 1): dp[i] = calculate_from_previous_statesAdvantages:
- Avoids recursion overhead.
- Often easier to optimize for memory.
Disadvantages:
- May compute states that are not needed.
- Requires knowing the correct order of computation.
---
Real-world example
A financial application calculates the maximum profit from buying and selling stocks over multiple days.
The problem can be solved with DP because:
- The best profit on day
ndepends on the best profits from previous days (optimal substructure). - The same previous-day calculations are needed repeatedly (overlapping subproblems).
Example:
def max_profit(prices): previous = 0 current = 0
for price in prices: best_today = max(current, previous + price) previous = current current = best_today
return currentThe algorithm stores previous results instead of recomputing possible decisions.
Common mistakes
- * Thinking that having smaller subproblems automatically means a problem can use DP.
- * Confusing overlapping subproblems with optimal substructure.
- * Forgetting that DP requires both properties together in most cases.
- * Using DP when subproblems are independent and do not repeat.
- * Assuming optimal substructure means a greedy solution will always work.
- * Ignoring repeated calculations in recursive solutions.
- * Storing results without identifying the correct DP state.
Follow-up questions
- What is the difference between overlapping subproblems and optimal substructure?
- Can a problem have overlapping subproblems but not be solved using DP?
- Can greedy algorithms have optimal substructure?
- How do you recognize a DP problem in an interview?
- Why is Fibonacci considered a classic DP problem?
- What happens if a problem only has optimal substructure but no overlapping subproblems?