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)

Informações da origem

Repositório
Tyler-R-Kendrick/agent-skills
Última atividade na origem
11 de fevereiro de 2026 às 05:14
Idioma detectado do SKILL.md
inglês
Estrelas
11
Forks
4

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Explorador de arquivos
12 arquivos

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
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.
Ver no GitHub