mirror of
https://github.com/prdlk/leetcode.git
synced 2026-09-17 07:26:27 +00:00
128 lines
3.1 KiB
Plaintext
128 lines
3.1 KiB
Plaintext
---
|
||
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:<br/>nums[i] + dp[i-2]"]
|
||
A --> C["Skip:<br/>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.
|