Skip to main content

dynamic-programming

Use when solving optimization problems with overlapping subproblems and optimal substructure. Covers memoization (top-down) vs tabulation (bottom-up), classic DP problems (Knapsack, LCS, LIS, Edit Distance, Coin Change, Matrix Chain, Rod Cutting), and the DP framework. Based on Knuth's TAOCP. USE FOR: optimization problems with overlapping subproblems, memoization strategies, tabulation approaches, recognizing DP problem patterns, state definition and recurrence formulation DO NOT USE FOR: graph shortest paths (use graph-algorithms), sorting (use sorting-searching)

Quellinformationen

Repository
Tyler-R-Kendrick/agent-skills
Letzte Quellaktivität
11. Februar 2026 um 05:14
Erkannte Sprache von SKILL.md
Englisch
Sterne
11
Forks
4

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
12 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
dynamic-programming
description
Use when solving optimization problems with overlapping subproblems and optimal substructure. Covers memoization (top-down) vs tabulation (bottom-up), classic DP problems (Knapsack, LCS, LIS, Edit Distance, Coin Change, Matrix Chain, Rod Cutting), and the DP framework. Based on Knuth's TAOCP. USE FOR: optimization problems with overlapping subproblems, memoization strategies, tabulation approaches, recognizing DP problem patterns, state definition and recurrence formulation DO NOT USE FOR: graph shortest paths (use graph-algorithms), sorting (use sorting-searching)
license
MIT
metadata
{"displayName":"Dynamic Programming","author":"Tyler-R-Kendrick"}
compatibility
claude, copilot, cursor
references
[{"title":"The Art of Computer Programming — Donald Knuth","url":"https://www-cs-faculty.stanford.edu/~knuth/taocp.html"},{"title":"Dynamic Programming — Wikipedia","url":"https://en.wikipedia.org/wiki/Dynamic_programming"}]
# Dynamic Programming ## Overview Dynamic programming (DP) is a method for solving problems by breaking them into overlapping subproblems, solving each subproblem once, and storing the results to avoid redundant computation. Knuth discusses dynamic programming techniques throughout *The Art of Computer Programming*, particularly in the context of optimization, sequence analysis, and combinatorial problems. The term was coined by Richard Bellman in the 1950s. ## Core Principles ### Optimal Substructure A problem exhibits optimal substructure if an optimal solution to the problem contains optimal solutions to its subproblems. This property allows us to build the global optimum from local optima. **Example**: The shortest path from A to C through B consists of the shortest path from A to B plus the shortest path from B to C. ### Overlapping Subproblems A problem has overlapping subproblems when the same subproblems are solved repeatedly in a naive recursive approach. DP eliminates this redundancy by storing results. **Example**: Computing Fibonacci(n) recursively recomputes Fibonacci(k) for each k < n exponentially many times. ## Two Approaches ### Memoization (Top-Down) Start with the original problem, recurse into subproblems, and cache results as they are computed. ``` FIB_MEMO(n, cache): if n <= 1: return n if n in cache: return cache[n] cache[n] = FIB_MEMO(n - 1, cache) + FIB_MEMO(n - 2, cache) return cache[n] ``` **Advantages**: Natural to write (follows recursive structure), computes only the subproblems actually needed. **Disadvantages**: Recursion overhead, potential stack overflow for deep recursion. ### Tabulation (Bottom-Up) Build a table from the smallest subproblems up to the desired result, iterating in a careful order. ``` FIB_TABLE(n): if n <= 1: return n dp[0] = 0, dp[1] = 1 for i = 2 to n: dp[i] = dp[i - 1] + dp[i - 2] return dp[n] ``` **Advantages**: No recursion overhead, easier to optimize space (often only need the last few entries). **Disadvantages**: May compute subproblems that are never needed, ordering can be less intuitive. ## The DP Framework When facing a potential DP problem, follow these steps: ### 1. Define the State Identify what information is needed to describe a subproblem. This becomes the index/key for your DP table. **Example** (Knapsack): `dp[i][w]` = maximum value using items 1..i with capacity w. ### 2. Write the Recurrence Express the solution to a subproblem in terms of smaller subproblems. **Example** (Knapsack): ``` dp[i][w] = max( dp[i-1][w], // skip item i dp[i-1][w - weight[i]] + value[i] // take item i (if weight[i] <= w) ) ``` ### 3. Identify the Base Case Define the values for the smallest subproblems that cannot be decomposed further. **Example** (Knapsack): `dp[0][w] = 0` for all w (no items means no value). ### 4. Determine the Build Order For tabulation, compute subproblems in an order such that all dependencies are resolved before they are needed. **Example** (Knapsack): Process items from i = 1 to n, capacities from w = 0 to W. ### 5. Extract the Answer The answer to the original problem is at a specific location in the DP table. **Example** (Knapsack): `dp[n][W]`. ### 6. (Optional) Optimize Space If the recurrence only depends on the previous row or a fixed number of prior entries, reduce the table accordingly. **Example** (Fibonacci): Only need dp[i-1] and dp[i-2], so use two variables instead of an array. ## Classic Problems ### Fibonacci Sequence | Approach | Time | Space | |----------|------|-------| | Naive recursion | O(2^n) | O(n) stack | | Memoization | O(n) | O(n) | | Tabulation | O(n) | O(n) or O(1) optimized | ### 0/1 Knapsack Given n items with weights and values, and a knapsack of capacity W, maximize the total value without exceeding the capacity. Each item can be taken at most once. - **State**: `dp[i][w]` = max value using first i items with capacity w - **Recurrence**: `dp[i][w] = max(dp[i-1][w], dp[i-1][w-wt[i]] + val[i])` - **Time**: O(n * W) - **Space**: O(n * W), or O(W) with rolling array ### Unbounded Knapsack Same as 0/1 Knapsack, but each item can be taken unlimited times. - **State**: `dp[w]` = max value with capacity w - **Recurrence**: `dp[w] = max(dp[w], dp[w-wt[i]] + val[i])` for each item i - **Time**: O(n * W) - **Space**: O(W) ### Longest Common Subsequence (LCS) Find the longest subsequence common to two sequences. - **State**: `dp[i][j]` = length of LCS of first i characters of X and first j characters of Y - **Recurrence**: ``` if X[i] == Y[j]: dp[i][j] = dp[i-1][j-1] + 1 else: dp[i][j] = max(dp[i-1][j], dp[i][j-1]) ``` - **Time**: O(m * n) - **Space**: O(m * n), or O(min(m, n)) optimized ### Longest Increasing Subsequence (LIS) Find the length of the longest strictly increasing subsequence. - **State**: `dp[i]` = length of LIS ending at index i - **Recurrence**: `dp[i] = max(dp[j] + 1)` for all j < i where A[j] < A[i] - **Time**: O(n^2), or O(n log n) with patience sorting (binary search on tails) - **Space**: O(n) ### Edit Distance (Levenshtein Distance) Minimum number of operations (insert, delete, replace) to transform one string into another. - **State**: `dp[i][j]` = edit distance between first i characters of X and first j characters of Y - **Recurrence**: ``` if X[i] == Y[j]: dp[i][j] = dp[i-1][j-1] else: dp[i][j] = 1 + min(dp[i-1][j], // delete dp[i][j-1], // insert dp[i-1][j-1]) // replace ``` - **Time**: O(m * n) - **Space**: O(m * n), or O(min(m, n)) optimized ### Coin Change Given coin denominations and a target amount, find the minimum number of coins needed (or the number of ways to make change). **Minimum coins:** - **State**: `dp[a]` = minimum coins to make amount a - **Recurrence**: `dp[a] = min(dp[a - coin] + 1)` for each coin denomination - **Base case**: `dp[0] = 0` - **Time**: O(amount * number_of_coins) - **Space**: O(amount) ### Matrix Chain Multiplication Find the optimal way to parenthesize a sequence of matrices to minimize total scalar multiplications. - **State**: `dp[i][j]` = minimum cost to multiply matrices i through j - **Recurrence**: `dp[i][j] = min(dp[i][k] + dp[k+1][j] + p[i-1]*p[k]*p[j])` for i <= k < j - **Base case**: `dp[i][i] = 0` - **Time**: O(n^3) - **Space**: O(n^2) ### Rod Cutting Given a rod of length n and prices for each length, find the maximum revenue from cutting the rod. - **State**: `dp[l]` = maximum revenue for rod of length l - **Recurrence**: `dp[l] = max(price[k] + dp[l - k])` for 1 <= k <= l - **Base case**: `dp[0] = 0` - **Time**: O(n^2) - **Space**: O(n) ## DP Problem Complexity Summary | Problem | Time | Space | |---------|------|-------| | Fibonacci | O(n) | O(1) optimized | | 0/1 Knapsack | O(n * W) | O(W) optimized | | Unbounded Knapsack | O(n * W) | O(W) | | LCS | O(m * n) | O(min(m, n)) optimized | | LIS | O(n log n) | O(n) | | Edit Distance | O(m * n) | O(min(m, n)) optimized | | Coin Change | O(amount * coins) | O(amount) | | Matrix Chain Mult. | O(n^3) | O(n^2) | | Rod Cutting | O(n^2) | O(n) | ## Memoization vs Tabulation: When to Use Which | Factor | Memoization (Top-Down) | Tabulation (Bottom-Up) | |--------|----------------------|----------------------| | Implementation style | Recursive + cache | Iterative + table | | Subproblem computation | Only those needed | All subproblems | | Stack overflow risk | Yes (deep recursion) | No | | Space optimization | Harder | Easier (rolling arrays) | | Code clarity | Often more intuitive | Requires careful ordering | | Performance | Function call overhead | Usually faster in practice | **Guideline**: Start with memoization for clarity and correctness, then convert to tabulation if performance or space optimization is needed. ## Recognizing DP Problems A problem is likely solvable with DP if: 1. It asks for an **optimal value** (min, max, count) or the **number of ways** to achieve something. 2. It has **overlapping subproblems** -- naive recursion recomputes the same states. 3. It has **optimal substructure** -- the optimal solution builds on optimal sub-solutions. 4. The problem can be parameterized by a **small set of variables** (the state space is manageable). ## Best Practices - Always verify optimal substructure before applying DP -- not all optimization problems have it (greedy or exhaustive search may be required instead). - Define your state precisely and minimally -- extra state dimensions explode the table size. - Validate your recurrence with small examples before coding. - Consider whether the problem admits a greedy solution (simpler) before committing to DP. - For interview/competition settings, practice identifying the state and recurrence quickly -- the implementation follows mechanically. - Reference Knuth's TAOCP for mathematical rigor on sequence problems, optimal search trees, and combinatorial optimization where DP techniques apply.
Auf GitHub ansehen