Back

Dynamic Programming

AWAKENING0 / 100
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░]0%

Dynamic Programming

Algorithms·advanced·~60 min read

  • dynamic-programming
  • dp
  • algorithm
  • pattern

Dynamic Programming (DP) is a technique for solving problems by breaking them into overlapping subproblems, solving each subproblem once, and storing results to avoid redundant computation. ---

What you'll learn

  • The Golden Rules for DP Identification

    Before jumping into categories, master these four questions. If you answer YES to all four, you almost certainly have a DP problem.

  • Rule 1: "Can I make a CHOICE at each step?"

    At every stage of the problem, you face a decision: - Include or exclude this item - Take or skip this element - Pick from the left end or the right end - Use this coin or dont use it - Match this character or skip it If the problem forc…

  • Rule 2: "Does making that choice leave me with a SMALLER version of the same problem?"

    This is optimal substructure. After making your choice, the remaining problem has the exact same structure, just smaller.

  • Rule 3: "Will I solve the same subproblem MULTIPLE times?"

    This is overlapping subproblems. Draw the recursion tree. If you see the same function call appearing in multiple branches, you have overlap. Without memoization, the same computation repeats exponentially.

  • Rule 4: "Am I asked for count/optimize/possibility?"

    DP problems almost always ask one of: - Count: "How many ways can you...?" - Optimize: "What is the minimum/maximum...?" - Possibility: "Can you...?" / "Is it possible to...?" If the problem asks you to enumerate all solutions or print a…

  • Top-Down (Memoization) vs Bottom-Up (Tabulation)

  • Top-Down: Start from the big problem, recurse down

    When to use top-down: - The state space is large but only a fraction is actually visited - The recursive structure is natural and easy to reason about - You want to quickly convert a brute-force recursive solution

  • Bottom-Up: Start from smallest subproblems, build up

    When to use bottom-up: - You need every subproblem solved (dense state space) - You want to optimize space (rolling arrays) - You want to avoid recursion stack overflow - Iterative solution is straightforward ---

  • The 6-Step DP Problem-Solving Process

    A systematic framework for converting any DP problem into working, optimized code: | Step | Action | Output | |------|--------|--------| | 1. Find Recurrence | Define what dp(i) means clearly; express answer for larger input in terms of…

  • State Design Methodology

    The hardest part of DP is defining your state. Ask: "What information do I need to make a decision at the current step?"

  • The "Consistency of Definition" Principle

    How you define dp(i) cascades through everything -- base cases, recurrence indexing, loop bounds, and answer location all change if the definition changes. This is the #1 source of off-by-one bugs in DP. Always write out what dp(i) means…

  • Step-by-step state design:

    1. Identify what changes between subproblems (index, remaining capacity, last character matched, etc.) 2. Each changing variable becomes a dimension of your DP table 3. Define what the state STORES: the answer to "given these parameters,…

  • Example: Knapsack

    - What changes? Current item index, remaining weight capacity - State: dp[i][w] = maximum value using items 0..i with capacity w - Can I compute from previous? Yes: either include item i or skip it

  • Common state patterns:

    | Pattern | State variables | |---------|----------------| | Linear sequence | Index i | | Two sequences | Index i in first, index j in second | | Knapsack variants | Index + remaining capacity | | Interval/range | Left endpoint, right e…

  • Transition Formula

  • How to find the recurrence relation

    Assume you already know the answer for all smaller inputs. For arrays: assume you know dp(0), dp(1), ..., dp(i-1). For strings: assume answers for all shorter prefixes. Then ask: "Can I use these known answers to compute dp(i) for the fu…

  • The transition encodes choices

    The transition (recurrence relation) defines how states connect. It encodes the CHOICES you can make. General template: ---

  • Category 1: 0/1 Knapsack Family

  • Core Idea

    You have items, each used at most once. For each item, you decide: INCLUDE or EXCLUDE.

  • Template

    Key insight: When we include item i, we look at dp[i-1][...] (previous row), ensuring each item is used only once.

  • Worked Example: Subset Sum

    Problem: Given a set of positive integers, determine if there exists a subset that sums to a target value. State: dp[i][s] = "Can we form sum s using items 0..i-1?" Transition: - Exclude item i: dp[i][s] = dp[i-1][s] - Include item i: dp…

  • Equal Partition Problem

    Problem: Can you partition an array into two subsets with equal sum? Reduction: If total sum is odd, impossible. Otherwise, find a subset summing to totalSum / 2. This is exactly subset sum.

  • Count of Subsets with Given Sum

    Variation: Instead of boolean (can/cant), count how many subsets sum to target. Change: dp[i][s] = dp[i-1][s] + dp[i-1][s - items[i]] (exclude count + include count) ---

  • Category 2: Unbounded Knapsack

  • Core Idea

    Items can be used unlimited times. The choice is: how many of each item to use.

  • Critical Difference from 0/1

    In 0/1 Knapsack, when including item i, you reference the previous row (dp[i-1][...]). In Unbounded Knapsack, when including item i, you reference the current row (dp[i][...]), allowing the same item to be picked again.

  • Space-Optimized 1D version

    For unbounded knapsack with 1D array, iterate capacity left to right (allows reuse): For 0/1 knapsack with 1D array, iterate capacity right to left (prevents reuse):

  • Worked Example: Coin Change (Minimum Coins)

    Problem: Given coin denominations and a target amount, find the minimum number of coins needed. State: dp[amount] = minimum coins to make this amount Cleaner trace:

  • Rod Cutting

    Problem: Cut a rod of length n to maximize profit. You have prices for each length 1..n. This is unbounded knapsack where "items" are pieces of different lengths, and "capacity" is the rod length. Each piece length can be used multiple t…

  • Category 3: Longest Common Subsequence (LCS) Family

  • Core Idea

    Compare two sequences character by character. At each position pair (i, j), decide: do these characters match, and what follows from that?

  • Template

  • Worked Example

    Fill logic: - Characters match: take diagonal + 1 (both characters are part of LCS) - Characters differ: take max of (skip from first, skip from second)

  • Longest Common Substring

    Difference from LCS: Characters must be contiguous. If characters dont match, reset to 0 instead of taking max of neighbors.

  • Edit Distance (Levenshtein Distance)

    Problem: Minimum operations (insert, delete, replace) to transform one string into another.

  • Wildcard / Regex Pattern Matching

    Problem: Given a string and a pattern with special characters (. matches any single character, means zero or more of the preceding element), determine if the pattern matches the entire string. Recursive Insight: 1. If the pattern is emp…

  • Shortest Common Supersequence

    Problem: Shortest string that has both strings as subsequences. Key insight: Length = len(s1) + len(s2) - LCS(s1, s2) The LCS characters are shared; everything else must appear once from each string. ---

  • Category 4: Longest Increasing Subsequence (LIS)

  • Core Idea

    Find the longest subsequence where each element is strictly greater than the previous.

  • O(n^2) DP Approach

    State: dp[i] = length of longest increasing subsequence ending at index i. Transition: For each j Intuition: We greedily keep the smallest possible tail for each length. A smaller tail gives us more room to extend later. Note: tails itse…

  • Category 5: Matrix Chain / Interval DP

  • Core Idea

    Partition a range into two subranges, solve each optimally, and combine. You try EVERY possible split point.

  • Template

  • The "Gap" Strategy for Filling

    Unlike row-by-row filling, interval DP fills by increasing subproblem size: This ensures when computing dp[i][j], all smaller subranges are already solved.

  • Worked Example: Matrix Chain Multiplication

    Problem: Given matrices with dimensions such that matrix i is dims[i] x dims[i+1], find the minimum number of scalar multiplications to compute their product.

  • Burst Balloons

    Problem: Given balloons with values, burst them one by one. When you burst balloon i, you get value[left] value[i] value[right]. Maximize total coins. Key insight: Think in reverse. Instead of "which to burst first," think "which to bu…

  • Palindrome Partitioning (Minimum Cuts)

    Problem: Minimum cuts to partition a string so every part is a palindrome. State: dp[i][j] = minimum cuts for substring [i..j] Transition: If s[i..j] is a palindrome, dp[i][j] = 0. Otherwise, try every split point. ---

  • Category 6: DP on Strings

  • Longest Palindromic Subsequence

    Problem: Find the longest subsequence of a string that is a palindrome. Key insight: LPS(s) = LCS(s, reverse(s)) Or directly:

  • Longest Palindromic Substring (O(n) — Expand with Sentinel Characters)

    Problem: Find the longest contiguous palindromic substring. Approach 1 — Expand Around Center (O(n^2)): For each of the 2n-1 centers (n char centers + n-1 gap centers), expand outward while characters match. Simple and often good enough.…

  • Word Break

    Problem: Can a string be segmented into words from a dictionary? State: dp[i] = "Can s[0..i-1] be segmented?" Transition: Try every possible last word ending at position i. ---

  • Category 7: DP on Trees

  • Core Idea

    Solve subproblems rooted at subtrees. Each node aggregates results from its children.

  • Template

  • Maximum Path Sum in Binary Tree

    Problem: Find the path with maximum sum. Path can start and end at any node. Key insight: At each node, you make two computations: 1. Best path passing THROUGH this node (uses both children) - updates global answer 2. Best path going UP…

  • House Robber on Tree

    Problem: Like house robber but on a binary tree. Cannot rob adjacent nodes (parent-child). ---

  • Space Optimization: Rolling Array

  • From 2D to 1D

    Many DP problems only need the previous row to compute the current row. We can reduce O(nm) space to O(m). Before (2 rows needed): After (rolling two rows):

  • From 2 Rows to True 1D (Knapsack style)

    For 0/1 knapsack, iterate right-to-left to simulate having the previous row: Right-to-left ensures dp[w - item.weight] still holds the "previous row" value (hasnt been updated yet in this pass). ---

  • Solution Traceback

    Often you need the actual solution, not just the optimal value.

  • Strategy 1: Store choices during DP fill

  • Strategy 2: Reconstruct from DP table values

    For LCS, trace back by checking which direction the value came from: ---

  • Category 8: Fibonacci / Linear DP

  • Core Idea

    Each state depends on a fixed number of immediately preceding states. The simplest form: dp[i] = f(dp[i-1], dp[i-2], ...).

  • Template

    Space: O(1) since only the last k states are needed.

  • Worked Example: Climbing Stairs

    Problem: You can climb 1 or 2 steps at a time. How many distinct ways to reach step n? This IS the Fibonacci sequence. The number of ways to reach step n = Fib(n+1).

  • House Robber (Linear)

    Problem: Array of house values. Cannot rob two adjacent houses. Maximize total. State: dp[i] = max money robbing from houses 0..i Transition: dp[i] = max(dp[i-1], dp[i-2] + nums[i]) — skip this house or rob it (must skip previous)

  • Decode Ways

    Problem: A message encoded as digits (1-26 → A-Z). Count ways to decode.

  • Problems in This Family

    | Problem | Recurrence | |---------|-----------| | Climbing Stairs | dp[i] = dp[i-1] + dp[i-2] | | House Robber | dp[i] = max(dp[i-1], dp[i-2] + val[i]) | | Decode Ways | dp[i] = dp[i-1] (if valid 1-digit) + dp[i-2] (if valid 2-digit) |…

  • Category 9: Grid DP

  • Core Idea

    Navigate a 2D grid from one corner to another (or fill the grid with values). Each cell depends on its neighbors (typically above and left).

  • Template

  • Worked Example: Unique Paths

    Problem: Count all paths from top-left to bottom-right, moving only right or down. First row and first column are all 1s (only one way to reach them).

  • Minimum Path Sum

    Problem: Find path from top-left to bottom-right minimizing sum of cells visited.

  • Unique Paths with Obstacles

    If grid has obstacles (cells marked 1), set dp[i][j] = 0 for those cells.

  • Maximal Square

    Problem: Find the largest square containing only 1s in a binary matrix. Key insight: dp[i][j] = side length of the largest square whose bottom-right corner is (i,j). A square of side k at (i,j) requires squares of side k-1 at all three n…

  • Problems in This Family

    | Problem | Key Variation | |---------|--------------| | Unique Paths | Count paths (right/down only) | | Unique Paths II | With obstacles | | Minimum Path Sum | Minimize sum on path | | Maximal Square | Largest 1-square in binary matrix…

  • Category 10: Kadane's Algorithm / Subarray DP

  • Core Idea

    At each position, decide: extend the current subarray or start a new one here. This "extend or restart" decision is the simplest form of DP.

  • Template

    State: dp[i] = maximum subarray sum ENDING at index i Transition: dp[i] = max(nums[i], dp[i-1] + nums[i]) — start fresh or extend

  • Worked Example: Maximum Subarray Sum

  • Maximum Circular Subarray

    Problem: The subarray can wrap around the end to the beginning. Key insight: Maximum circular subarray = max(standard Kadane, totalSum - minimum subarray). If the max sum wraps around, then the elements NOT in the subarray form a contigu…

  • Maximum Product Subarray

    Variation: Track both max and min at each position (a negative times a negative becomes positive). Why track min? A large negative product can become the largest positive when multiplied by the next negative number.

  • Problems in This Family

    | Problem | Twist | |---------|-------| | Maximum Subarray Sum | Classic Kadane | | Maximum Circular Subarray | max(Kadane, total - minKadane) | | Maximum Product Subarray | Track both max and min products | | Minimum Subarray Sum | Nega…

  • 0/1 Knapsack: Additional Worked Variations

  • Minimum Subset Sum Difference

    Problem: Partition array into two subsets to minimize the absolute difference of their sums. Reduction: If totalSum = S, we need subset with sum as close to S/2 as possible. Then min difference = S - 2bestSum.

  • Target Sum (Assign + or - to Each Element)

    Problem: Assign + or - signs to each element to reach a target sum. Count the number of ways. Reduction: If S1 is the sum of elements with +, S2 is the sum with -: - S1 + S2 = totalSum - S1 - S2 = target - Therefore: S1 = (totalSum + tar…

  • Count Subsets with Given Difference

    Problem: Count pairs of subsets (S1, S2) where S1 - S2 = diff. Reduction: S1 + S2 = totalSum, S1 - S2 = diff → S1 = (totalSum + diff) / 2 Then count subsets summing to S1. Identical to Target Sum after the reduction. ---

  • Quick Reference: Identifying the Category

    ---

  • Common Pitfalls

    1. Forgetting base cases: Always initialize dp[0], dp[empty], dp[single element] correctly 2. Off-by-one in indices: dp tables often have size n+1 to accommodate the empty prefix 3. Wrong loop direction: 0/1 knapsack needs right-to-left…

  • Complexity Cheat Sheet

    | Category | Time | Space (optimized) | |----------|------|-------------------| | 0/1 Knapsack | O(n capacity) | O(capacity) | | Unbounded Knapsack | O(n capacity) | O(capacity) | | LCS | O(m n) | O(min(m, n)) | | LIS (DP) | O(n^2) |…

  • Advanced DP Patterns

  • Multi-Dimensional DP with Constraints — Counting Stable Arrays

    Problem type: Count arrangements of zeros and ones in a binary array such that no consecutive run of same value exceeds a given limit. State: dp[i][j][last] = number of ways to place i zeros and j ones, where the last placed digit is las…

  • Maximum Strength from K Disjoint Subarrays

    Problem: Select exactly K non-overlapping subarrays to maximize a weighted "strength" formula where each subarray's sum is multiplied by a coefficient that alternates sign and decreases in magnitude. State: dp[i][j] = maximum strength ob…

  • Summary: The DP Solving Framework

    1. Recognize the problem type using the Golden Rules 2. Identify which category it falls into 3. Define state: What parameters capture the subproblem? 4. Write transition: What choices do I have? How do states connect? 5. Determine order…

← back to Algorithms

Dynamic Programming — Algorithms | GoCrack | GoCrack