---
id: wiki-2026-0508-dynamic-programming
title: Dynamic Programming
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [DP, 동적 계획법, 동적 프로그래밍]
duplicate_of: none
source_trust_level: A
confidence_score: 0.95
verification_status: applied
tags: [algorithms, dp, optimization, memoization, tabulation]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: python
  framework: none
---

# Dynamic Programming

## 매 한 줄
> **"매 overlapping subproblem 의 cache 의 의 의 exponential → polynomial"**. 매 1953 Bellman 의 명명 ("dynamic" 의 RAND 의 selling reason). 매 modern algo 의 universal tool — 매 Bioinformatics, ML training, RL value iteration 의 base.

## 매 핵심

### 매 두 조건 (DP applicability)
1. **Optimal substructure**: 매 optimal solution 의 sub-optimal solution 의 의 build.
2. **Overlapping subproblems**: 매 same subproblem 의 repeatedly 의 solve.

### 매 두 style
- **Top-down (memoization)**: recursion + cache. 매 declarative, lazy.
- **Bottom-up (tabulation)**: iterative table fill. 매 stack-safe, faster constant.

### 매 state design
- **state**: 매 minimal info 의 의 의 subproblem 의 identify.
- **transition**: 매 state → state 의 recurrence.
- **base case**: 매 smallest 의 known answer.
- **answer**: 매 final state 의 retrieve.

### 매 응용
1. Bioinformatics: sequence alignment (Needleman-Wunsch).
2. NLP: edit distance, CKY parsing, Viterbi.
3. RL: Bellman value iteration.
4. Graph: Floyd-Warshall, Bellman-Ford.
5. Compiler: register allocation, instruction scheduling.

## 💻 패턴

### Fibonacci (intro example)
```python
from functools import lru_cache

# Top-down
@lru_cache(maxsize=None)
def fib(n):
    if n < 2: return n
    return fib(n-1) + fib(n-2)

# Bottom-up O(1) space
def fib_bu(n):
    if n < 2: return n
    a, b = 0, 1
    for _ in range(n - 1):
        a, b = b, a + b
    return b
```

### 0/1 Knapsack
```python
def knapsack(weights: list[int], values: list[int], capacity: int) -> int:
    n = len(weights)
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for w in range(capacity + 1):
            if weights[i-1] <= w:
                dp[i][w] = max(dp[i-1][w], dp[i-1][w - weights[i-1]] + values[i-1])
            else:
                dp[i][w] = dp[i-1][w]
    return dp[n][capacity]
# Time O(n·W), Space O(n·W) — can compress to O(W) with reverse iteration
```

### Longest Common Subsequence (LCS)
```python
def lcs(a: str, b: str) -> int:
    n, m = len(a), len(b)
    dp = [[0] * (m + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for j in range(1, m + 1):
            if a[i-1] == b[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    return dp[n][m]
```

### Edit Distance (Levenshtein)
```python
def edit_distance(a: str, b: str) -> int:
    n, m = len(a), len(b)
    dp = [[0] * (m + 1) for _ in range(n + 1)]
    for i in range(n + 1): dp[i][0] = i
    for j in range(m + 1): dp[0][j] = j
    for i in range(1, n + 1):
        for j in range(1, m + 1):
            if a[i-1] == b[j-1]:
                dp[i][j] = dp[i-1][j-1]
            else:
                dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
    return dp[n][m]
```

### Coin Change (min coins)
```python
def coin_change(coins: list[int], amount: int) -> int:
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0
    for x in range(1, amount + 1):
        for c in coins:
            if c <= x:
                dp[x] = min(dp[x], dp[x - c] + 1)
    return dp[amount] if dp[amount] != float('inf') else -1
```

### Bitmask DP (TSP)
```python
def tsp(dist: list[list[int]]) -> int:
    n = len(dist)
    INF = float('inf')
    dp = [[INF] * n for _ in range(1 << n)]
    dp[1][0] = 0  # start at 0 with mask {0}
    for mask in range(1, 1 << n):
        for u in range(n):
            if not (mask & (1 << u)) or dp[mask][u] == INF: continue
            for v in range(n):
                if mask & (1 << v): continue
                new_mask = mask | (1 << v)
                dp[new_mask][v] = min(dp[new_mask][v], dp[mask][u] + dist[u][v])
    return min(dp[(1 << n) - 1][u] + dist[u][0] for u in range(1, n))
# O(n²·2ⁿ) — feasible for n ≤ 20
```

## 매 결정 기준
| 상황 | Approach |
|---|---|
| 매 overlapping subproblem 의 detect | DP |
| 매 unique subproblem (no overlap) | divide & conquer |
| 매 greedy 의 optimal proof 가능 | greedy (faster, less memory) |
| 매 state 의 huge | approximate DP / RL |
| 매 small recursion depth | top-down (cleaner) |
| 매 deep recursion 의 우려 | bottom-up |
| 매 memory 의 tight | rolling array (O(prev) 의 의) |

**기본값**: 매 first attempt 의 top-down + memo. 매 deep / huge 의 의 의 bottom-up.

## 🔗 Graph
- 부모: [[Optimization]]
- 변형: [[Memoization]]
- Adjacent: [[Greedy Algorithms]]

## 🤖 LLM 활용
**언제**: 매 optimization problem 의 overlapping subproblem 의 detect, 매 string/sequence problem, 매 counting problem (combinatorial), 매 RL value function 의 의.
**언제 X**: 매 unique subproblem (D&C 의 의), 매 greedy 의 proven optimal, 매 huge state space 의 approximation 의 의 (NN, MCTS).

## ❌ 안티패턴
- **state 의 over-include**: 매 unnecessary dim 의 의 의 의 exponential blow-up.
- **base case X**: 매 infinite recursion / wrong result.
- **mutable default args** (Python): `def f(x, memo={})` 의 cross-call leak.
- **bottom-up 의 wrong order**: 매 state 의 dependency 의 의 의 의 의 fill 의 X.
- **`@lru_cache` with mutable args**: list / dict 의 의 의 hash error — tuple 의 의.

## 🧪 검증 / 중복
- Verified (Bellman 1957 "Dynamic Programming"; CLRS Ch 15; Kleinberg-Tardos Ch 6).
- 신뢰도 A.

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — DP foundations with knapsack, LCS, edit distance, bitmask TSP |
