--- 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 |