--- title: Dynamic Programming (1-D) --- # Dynamic Programming (1-D) ## The idea DP is backtracking with a memory. Backtracking tries every path. Many paths repeat the same subproblem. DP solves each subproblem **once**, saves the answer, and reuses it. Grokking's rule: break the big problem into small problems. Solve the small ones first. Build up. **The two things you must find:** 1. **The state** — what does `dp[i]` mean, in one sentence? 2. **The recurrence** — how does `dp[i]` come from earlier answers? ## The picture — why memo matters (Climbing Stairs) Without memo, `f(5)` computes `f(3)` twice and `f(2)` three times: ```mermaid flowchart TD A["f(5)"] --> B["f(4)"] A --> C["f(3)"] B --> D["f(3)"] B --> E["f(2)"] C --> F["f(2)"] C --> G["f(1)"] D --> H["f(2)"] D --> I["f(1)"] style C fill:#c62828,color:#fff style D fill:#c62828,color:#fff style E fill:#ef6c00,color:#fff style F fill:#ef6c00,color:#fff style H fill:#ef6c00,color:#fff ``` Red and orange = repeated work. Memo turns the tree into a straight line: O(2ⁿ) → O(n). ## Climbing Stairs (LC 70) State: `dp[i]` = ways to reach step i. Recurrence: you arrive from one step below or two below. ```python def climb_stairs(n: int) -> int: if n <= 2: return n prev2, prev1 = 1, 2 for _ in range(3, n + 1): prev2, prev1 = prev1, prev1 + prev2 return prev1 ``` ## House Robber (LC 198) State: `dp[i]` = max loot using houses 0..i. Recurrence at each house: **rob it** (skip the neighbor) or **skip it**. ```mermaid flowchart LR A["House i"] --> B["Rob:
nums[i] + dp[i-2]"] A --> C["Skip:
dp[i-1]"] B --> D["dp[i] = max of both"] C --> D style D fill:#2e7d32,color:#fff ``` ```python def rob(nums: list[int]) -> int: skip = take = 0 for n in nums: skip, take = max(skip, take), skip + n # skip it / rob it return max(skip, take) ``` ## Coin Change (LC 322) State: `dp[a]` = fewest coins to make amount a. Recurrence: try each coin, take the best. ```python import math def coin_change(coins: list[int], amount: int) -> int: dp = [math.inf] * (amount + 1) dp[0] = 0 for a in range(1, amount + 1): for c in coins: if c <= a: dp[a] = min(dp[a], dp[a - c] + 1) return dp[amount] if dp[amount] != math.inf else -1 ``` ## The interview script Say these four lines out loud, in order: 1. "The brute force is backtracking — try everything." 2. "Subproblems overlap, so I will memoize." 3. "State: dp[i] means ___." (one sentence) 4. "Recurrence: dp[i] = ___." ## Complexity | | Time | Space | |---|---|---| | Climbing Stairs / House Robber | O(n) | O(1) with rolling vars | | Coin Change | O(amount × coins) | O(amount) | ## Close-out ritual Before you submit, say out loud: 1. Time and space complexity. 2. One edge case trace: n = 0 or 1, empty array, or unreachable amount (return -1). ## Plan problems **A-set:** LC 70 · 746 · 198 · 213 · 322 · 300 **B-set:** LC 139 · 91 · 647 Note: DP matters only if Google or Databricks advance to later rounds. Your fintech targets skew toward simulation, hashmap, and heap.