Mastering Dynamic Programming: Types, Patterns & Practice Questions
Dynamic Programming (DP) is a powerful technique used to solve problems with overlapping subproblems and optimal substructure. Below is a structured guide for solving each type, with patterns and 5 representative LeetCode problems per category.
1. 0/1 Knapsack DP
✅ When to Use:
-
Choose or skip items.
-
Once an item is taken, it can't be reused.
⚖️ Pattern:
dp[i][w] = max(dp[i-1][w], dp[i-1][w - wt[i]] + val[i])
🔹 Steps:
-
Define
dp[i][w]= max value using firstiitems with capacityw. -
Base case:
dp[0][w] = 0 -
Iterate
ifrom 1 to n, andwfrom 0 to capacity.
⚡ Problems:
-
198. House Robber (Variant)
2. Unbounded Knapsack DP
✅ When to Use:
- You can take the same item multiple times.
⚖️ Pattern:
dp[w] = max(dp[w], dp[w - wt[i]] + val[i])
🔹 Steps:
-
Use 1D
dpof sizeW+1. -
Outer loop on items, inner loop on weights.
⚡ Problems:
3. Linear DP (Fibonacci Style)
✅ When to Use:
- Each state depends only on last 1-2 (or k) states.
⚖️ Pattern:
dp[i] = max(dp[i-1], nums[i] + dp[i-2])
🔹 Steps:
-
Identify base cases.
-
Use rolling variables or full DP array.
⚡ Problems:
4. Interval DP
✅ When to Use:
- Problem involves splitting/merging subarrays or segments.
⚖️ Pattern:
dp[i][j] = min/max over dp[i][k] + dp[k][j] + cost(i,j)
🔹 Steps:
-
Sort the array if needed.
-
Build DP table bottom-up over interval length.
⚡ Problems:
5. Tree/Graph DP
✅ When to Use:
- DP over a tree structure or graph with topological order.
⚖️ Pattern:
dp[node] = function of dp[children]
🔹 Steps:
-
Use DFS to traverse tree.
-
At each node, compute DP from children.
⚡ Problems:
6. Bitmask / Digit DP
✅ When to Use:
-
Bitmask: Subsets, permutations with memory of state.
-
Digit: Count numbers under digit constraints.
⚖️ Bitmask Pattern:
dp[mask][i] = min cost of visiting set mask ending at i
⚖️ Digit Pattern:
dp[pos][tight][leadingZero] = count
⚡ Problems:
7. 2D Grid/String DP
✅ When to Use:
- Substring/subsequence/2D matrix traversal problems.
⚖️ Pattern:
dp[i][j] = function(dp[i-1][j], dp[i][j-1], ...) depending on characters
🔹 Steps:
-
Set up 2D array with base cases.
-
Fill based on string/grid transitions.
⚡ Problems:
🔪 Pro Tip:
Before jumping to code:
-
Can you define state variables?
-
Can you write a recurrence?
-
Is there optimal substructure and overlap?
If yes to all: DP is likely your tool.
Let me know if you want code templates or problem walkthroughs!
Unbounded KnapSack with Order (Review)
class Solution {
public:
int countWays(vector<int>& nums, int target) {
if (target == 0) return 1;
int total = 0;
for (int num : nums) {
if (num <= target) {
total += countWays(nums, target - num);
}
}
return total;
}
int combinationSum4(vector<int>& nums, int target) {
return countWays(nums, target);
}
};