Home/Learn/Dynamic Programming

Pattern Guide

Dynamic Programming

"Overlapping subproblems + optimal substructure = DP."

DP is not magic. It's recursion with memoization, then turned into a table. Learn the 4-step progression from brute force to space-optimized tabulation, and the classic problem families.

24 min readdp problems →

Problems you can solve with this pattern

13 problems · click any to start solving

All dp
1House RobberMediumSolve
2Minimum Cost Path (DP on 2D grid)MediumSolve
3Coin ChangeMediumSolve
4Maximum Sum of 3 Non-Overlapping SubarraysHardSolve
Step 1 → Brute force recursion
// No memoization. Time: O(2^n)
const helper = (i) => {
    if (i >= n) return 0;
    return Math.max(helper(i + 1), nums[i] + helper(i + 2));
};

Dynamic programming has a reputation for being mysterious, but there's a mechanical progression that works on almost every DP problem. The key is recognizing that DP is just recursion where you cache intermediate results to avoid recomputing them. Once you have the recursive solution, turning it into tabulation is almost mechanical.

Two requirements for DP:
1. Optimal substructure — optimal solution of the whole can be built from optimal solutions of subproblems
2. Overlapping subproblems — same subproblems are solved multiple times in the naive recursion

The 4-Step Progression

Step 1 → Brute force recursion
// No memoization. Time: O(2^n)
const helper = (i) => {
    if (i >= n) return 0;
    return Math.max(helper(i + 1), nums[i] + helper(i + 2));
};
Step 2 → Memoization (top-down)
// Cache results. Time: O(n), Space: O(n)
const mem = {};
const helper = (i) => {
    if (i >= n) return 0;
    if (mem[i] !== undefined) return mem[i];
    return mem[i] = Math.max(helper(i + 1), nums[i] + helper(i + 2));
};
Step 3 → Tabulation (bottom-up)
// Build table iteratively. Time: O(n), Space: O(n)
const dp = new Array(n + 2).fill(0);
for (let i = n - 1; i >= 0; i--)
    dp[i] = Math.max(dp[i + 1], nums[i] + dp[i + 2]);
return dp[0];
Step 4 → Space optimization
// Only need last two values. Time: O(n), Space: O(1)
let next1 = 0, next2 = 0;
for (let i = n - 1; i >= 0; i--) {
    const curr = Math.max(next1, nums[i] + next2);
    next2 = next1;
    next1 = curr;
}
return next1;
DAG visualization trick: Draw the recursion tree as a DAG (Directed Acyclic Graph) — each unique subproblem is one node. The DAG shape tells you:
- Shape of the DP table (dimensions)
- What subproblems each problem needs
- Rotate DAG 90° clockwise → it looks like the DP table filling order

Problem Families

FamilyKey WordsState Shape
1D linearFibonacci, house robber, climbing stairsdp[i]
2D gridMin path, unique paths, coin change (unbounded)dp[i][j]
Knapsack0/1 take or skip, weight/valuedp[i][w]
Interval DPMerge, burst, cut, matrix chaindp[i][j] = range [i..j]
Tree DPHouse robber III, max path sumpair [skip, rob] from postorder
Bitmask DPAssigned tasks, TSPdp[mask][i]
Digit DPCount numbers with property ≤ Ndp[pos][tight][state]

DP on Strings (Edit Distance Family)

Template — 2D DP on two strings s and t:
``
dp[i][j] = answer for s[0..i-1] and t[0..j-1]
``
- If s[i-1] === t[j-1]: dp[i][j] = dp[i-1][j-1] + (something)
- Else: dp[i][j] = min/max of dp[i-1][j], dp[i][j-1], dp[i-1][j-1] + (cost)

Edit distance, LCS, shortest common supersequence, wildcard matching all use this skeleton.

DP on Intervals

Interval DP template: dp[i][j] = answer for subproblem on range [i, j].

Fill by increasing length: for len in 2..n → for i in 0..n-len → j = i+len-1 → try all split points k.

Used for: burst balloons, matrix chain multiplication, palindrome partitioning, optimal BST.

Knapsack Pattern

0/1 Knapsack template: dp[i][w] = max value using first i items with capacity w.
- Include item i: dp[i-1][w-weight[i]] + value[i]
- Exclude item i: dp[i-1][w]
- Take max of both

1D optimization: iterate capacity backwards when each item can only be used ONCE (0/1). Iterate forwards for unbounded knapsack (coin change).
DP pattern families:
- 1D linear: Fibonacci, climbing stairs, house robber, coin change, Kadane's
- 2D grid: unique paths, minimum path sum, dungeon game
- 2D string: edit distance, LCS, regex, wildcard matching
- Interval: burst balloons, matrix chain, palindrome partition
- Knapsack: 0/1 (iterate j backwards), unbounded (forwards), count variants
- Bitmask DP: traveling salesman, min cost to visit all nodes

Recognizing DP: words like *shortest, longest, minimum, maximum, count, ways, best* — especially with a constraint like "at most k" or "contiguous".

Knapsack direction rule: iterate weight/sum backwards for 0/1 (each item once), forwards for unbounded (items reusable).