Overview
Dynamic Programming (DP) is a powerful algorithm design technique for solving optimization problems by breaking them down into simpler overlapping subproblems. Unlike divide and conquer, DP stores the results of subproblems to avoid redundant calculations.The key insight of dynamic programming: “Those who cannot remember the past are condemned to repeat it.” By remembering solutions to subproblems, we dramatically improve efficiency.
Core Concept
As described in GeeksforGeeks:“Whenever we have recursive function calls with the same result, instead of calling them again we try to store the result in a data structure in the form of a table and retrieve the results from the table. Thus, the overall time complexity is reduced. ‘Dynamic’ means we dynamically decide, whether to call a function or retrieve values from the table.”
Two Approaches
Top-Down (Memoization)
Start with the original problem and recursively break it down, storing results in a cache
Bottom-Up (Tabulation)
Start with the smallest subproblems and build up to the original problem using a table
Problem: Longest Common Subsequence (LCS)
Problem Description
Problem Description
Given two sequences, find the length of the longest subsequence present in both of them. A subsequence is a sequence that appears in the same relative order, but not necessarily contiguous.Example:Other examples of subsequences:
- For
abcdefg:abc,abg,bdf,aeg,acefgare all valid subsequences
Solution Approach
Solution Approach
We’ll use a 2D table where
dp[i][j] represents the length of LCS for the first i characters of string A and first j characters of string B.Recurrence relation:- If
strA[i-1] == strB[j-1]:dp[i][j] = dp[i-1][j-1] + 1 - Otherwise:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
Implementation
Implementation
Complexity Analysis
Complexity Analysis
- Time Complexity: O(m × n)
- Where m and n are the lengths of the two strings
- We fill each cell of the matrix exactly once
- Space Complexity: O(m × n)
- For the DP table
- Can be optimized to O(min(m, n)) by keeping only two rows
Space Optimization
Space Optimization
Since we only need the previous row to compute the current row, we can optimize space:This reduces space complexity to O(min(m, n)).
When to Use Dynamic Programming
Use When
- Problem has overlapping subproblems
- Problem has optimal substructure
- Need to find optimal solution (min/max)
- Counting problems (number of ways)
- Can define recurrence relation
Avoid When
- Subproblems are independent (use D&C)
- Greedy choice property exists
- Memory is severely constrained
- Problem doesn’t have optimal substructure
- Simpler iterative solution exists
Identifying DP Problems
Check for Optimal Substructure
Can the optimal solution be constructed from optimal solutions of subproblems?
Write Recurrence Relation
How does the solution to a problem relate to solutions of smaller problems?
Classic DP Problem Patterns
1. Linear DP (1D)
1. Linear DP (1D)
Examples: Fibonacci, Climbing Stairs, House Robber
2. Grid DP (2D)
2. Grid DP (2D)
Examples: Longest Common Subsequence, Edit Distance, Unique Paths
3. Knapsack Problems
3. Knapsack Problems
Examples: 0/1 Knapsack, Subset Sum, Partition Equal Subset Sum
4. Interval DP
4. Interval DP
Examples: Matrix Chain Multiplication, Palindrome Partitioning
5. Tree DP
5. Tree DP
Examples: House Robber III, Binary Tree Maximum Path Sum
DP Strategy: The Four Steps
Memoization vs Tabulation
| Aspect | Memoization (Top-Down) | Tabulation (Bottom-Up) |
|---|---|---|
| Direction | Recursive, starts from main problem | Iterative, starts from base cases |
| Implementation | Easier to write from recursion | Requires careful ordering |
| Space | Only computes needed subproblems | Computes all subproblems |
| Stack | Uses recursion stack | No recursion overhead |
| Debugging | Harder to debug | Easier to trace |
Common DP Optimizations
Space Optimization
Space Optimization
If
dp[i] only depends on dp[i-1] and dp[i-2], use two variables instead of an array:Rolling Array
Rolling Array
For 2D DP, use two rows instead of full matrix:
State Compression
State Compression
Use bitmasking for subset-based states:
Tips and Best Practices
Related Topics
Divide and Conquer
For independent subproblems
Greedy Algorithms
For problems with greedy choice property
Backtracking
For exploring all possibilities