Dynamic Programming Solution to the Knapsack Problem
Problem Description
The knapsack problem is a classic combinatorial optimization problem: given a set of items, each with a weight and a value, select a subset of items to maximize the total value without exceeding the knapsack's weight capacity. Formally described as:
- Knapsack capacity: \(W\)
- Number of items: \(n\)
- Weight of the \(i\)-th item: \(w_i\), value: \(v_i\)
- Objective: Select a subset of items satisfying \(\sum w_i \leq W\) and maximizing \(\sum v_i\).
Solution Approach
Dynamic Programming solves the problem by breaking it down into subproblems. Defining the state and the state transition equation is key.
Step 1: Define State
Let \(dp[i][j]\) represent the maximum value achievable when considering the first \(i\) items (items numbered from 1 to i) and with a knapsack capacity of \(j\).
Step 2: Initialization
- When the number of items is 0 (\(i=0\)) or the knapsack capacity is 0 (\(j=0\)), the maximum value is 0:
\(dp[0][j] = 0\), \(dp[i][0] = 0\).
Step 3: State Transition Equation
For each item \(i\) and each capacity \(j\):
- If the current item's weight \(w_i > j\): Cannot be taken. Inherit the result from the first \(i-1\) items:
\(dp[i][j] = dp[i-1][j]\). - If \(w_i \leq j\): There are two choices:
- Do not take it: Value is \(dp[i-1][j]\).
- Take it: Value is \(dp[i-1][j - w_i] + v_i\) (maximum value for remaining capacity \(j-w_i\) plus the current item's value).
Take the maximum of the two:
\(dp[i][j] = \max(dp[i-1][j], dp[i-1][j - w_i] + v_i)\).
Step 4: Tabulation
Iterate sequentially through \(i\) from 1 to \(n\) and \(j\) from 1 to \(W\), filling the 2D array \(dp\). The final answer is at \(dp[n][W]\).
Step 5: Space Optimization
Since \(dp[i][j]\) only depends on data from the previous row \(i-1\), the 2D array can be optimized to a 1D array \(dp[j]\):
- Traverse \(j\) in reverse order from \(W\) down to \(w_i\) (to avoid overwriting data from the previous row):
\(dp[j] = \max(dp[j], dp[j - w_i] + v_i)\).
Example Demonstration
Assume the item information is as follows (\(n=4, W=8\)):
| Item | Weight \(w_i\) | Value \(v_i\) |
|---|---|---|
| 1 | 2 | 3 |
| 2 | 3 | 4 |
| 3 | 4 | 5 |
| 4 | 5 | 6 |
Optimized 1D DP Calculation Process (Initial \(dp[j] = 0\)):
- Item 1 (weight 2, value 3):
- \(j=8\) down to \(2\): \(dp[8] = \max(0, dp[6]+3)=3\), similarly update \(dp[7]\) down to \(dp[2]\) to 3.
- Item 2 (weight 3, value 4):
- \(j=8\): \(dp[8] = \max(3, dp[5]+4)=7\) (taking items 2 and 1).
- Update other capacities similarly.
- Items 3 and 4: Continue updating analogously. The final result is \(dp[8] = 10\) (selecting items 2 and 4, weight 3+5=8, value 4+6=10).
Key Points Summary
- The core of dynamic programming is overlapping subproblems and optimal substructure.
- Space optimization requires attention to the traversal order to avoid recomputation.
- Time complexity: \(O(nW)\), Space complexity: \(O(W)\).