Knapsack DP
DP for the knapsack problem, including 0-1, complete, and multiple knapsack variations.
Knapsack DP
Setup:
- Input: items, each with weight and value , and a knapsack with capacity , .
- Output: The maximum total value of items that can be put in the knapsack without exceeding the capacity.
0-1 Knapsack (only one)
Steps:
- Subproblem: be the maximum total value of items that can be put in a knapsack with capacity using the first items.
- Recurrence relation:
- Answer: .
Optimized recurrence relation (for each item , from down to , so each item is used at most once):
int Knapsack01(int n, int W, vector<int> w, vector<int> v) {
vector<int> f(W + 1, 0);
for (int i = 1; i <= n; i++)
for (int j = W; j >= w[i]; j--)
f[j] = max(f[j], f[j - w[i]] + v[i]);
return f[W];
}
int Knapsack01(int n, int W, vector<int> w, vector<int> v) {
vector<vector<int>> f(n + 1, vector<int>(W + 1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= W; j++) {
if (j < w[i]) {
f[i][j] = f[i - 1][j];
} else {
f[i][j] = max(f[i - 1][j], f[i - 1][j - w[i]] + v[i]);
}
}
}
return f[n][W];
}
Complete Knapsack (infinite)
Steps:
- Subproblem: be the maximum total value of items that can be put in a knapsack with capacity .
- Recurrence relation:
- Answer: .
int KnapsackComplete(int n, int W, vector<int> w, vector<int> v) {
vector<int> f(W + 1, 0);
for (int i = 1; i <= n; i++)
for (int j = w[i]; j <= W; j++)
f[j] = max(f[j], f[j - w[i]] + v[i]);
return f[W];
}
Multiple Knapsack
Item can be used at most times.
int KnapsackMultiple(int n, int W, vector<int> w, vector<int> v, vector<int> m) {
vector<int> f(W + 1, 0);
for (int i = 1; i <= n; i++) {
for (int j = W; j >= w[i]; j--) {
for (int k = 1; k <= m[i] && k * w[i] <= j; k++) {
f[j] = max(f[j], f[j - k * w[i]] + k * v[i]);
}
}
}
return f[W];
}
Binary splitting: split into ; these pieces add up to every count in , so each piece becomes one 0-1 item. If , item is effectively unlimited.
int KnapsackMultiple(int n, int W, vector<int> w, vector<int> v, vector<int> m) {
vector<int> f(W + 1, 0);
for (int i = 1; i <= n; i++) {
if (w[i] * m[i] >= W) {
for (int j = w[i]; j <= W; j++)
f[j] = max(f[j], f[j - w[i]] + v[i]);
} else {
int k = 1, cnt = m[i];
while (k < cnt) {
for (int j = W; j >= k * w[i]; j--)
f[j] = max(f[j], f[j - k * w[i]] + k * v[i]);
cnt -= k;
k <<= 1;
}
for (int j = W; j >= cnt * w[i]; j--)
f[j] = max(f[j], f[j - cnt * w[i]] + cnt * v[i]);
}
}
return f[W];
}
Variations
Knapsack by value
Steps:
- Subproblem: be the minimum total weight of items that can achieve total value using the first items.
- Recurrence relation:
- Answer: .
Optimized recurrence relation:
int KnapsackByValue(int n, int W, vector<int> w, vector<int> v) {
int V = accumulate(v.begin(), v.end(), 0);
vector<int> f(V + 1, INT_MAX / 2); // INT_MAX + w[i] would overflow
f[0] = 0;
for (int i = 1; i <= n; i++)
for (int j = V; j >= v[i]; j--)
f[j] = min(f[j], f[j - v[i]] + w[i]);
for (int j = V; j >= 0; j--)
if (f[j] <= W)
return j;
return 0;
}