Dynamic Programming · 37 of 60
Unbounded Knapsack (Repetition of items allowed)
Fill a bag of fixed size with the most valuable mix of items when every item can be taken as many times as you like.
- Simple way
- time O(n^W)
room O(W) - Clever way
- time O(n * W)
room O(W)
The problem, simply
You have a bag that can hold a total weight of W. There are n kinds of items. Each kind has a weight and a value, and the shop never runs out, so you may take any kind as many times as you want. Give back the biggest total value that fits in the bag.
This is the knapsack problem with one change: items can repeat.
A story to remember it
Picture it
Four kinds of items, each available as often as you like. The best bag of size 8 holds one weight 3 item and one weight 5 item: 40 + 70 = 110.
- answer
Examples
The shop
- In
W = 8, weight = [1, 3, 4, 5], value = [10, 40, 50, 70]- Out
110
3 + 5 gives 40 + 70. Two 4s give only 100, and eight 1s give 80.
Best value per weight is a trap
- In
W = 10, weight = [5, 6], value = [10, 13]- Out
20
The weight 6 item has the better value per weight, but after one of them only 4 is left, which fits nothing. Two weight 5 items give 20.
Lots of copies
- In
W = 100, weight = [1, 50], value = [1, 30]- Out
100
A hundred weight 1 items beat two weight 50 items (60).
Nothing fits
- In
W = 2, weight = [3, 4], value = [5, 6]- Out
0
Every item is heavier than the bag.
Empty bag
- In
W = 0- Out
0
No room, no value.
Way 1: the simple way
best(cap) is the most value a bag of size cap can hold.
- Try every item that fits as the last one in the bag.
- It is worth its value plus
best(cap - weight), the best for the room that is left. - Keep the biggest. If nothing fits, the answer is 0.
The calls for a bag of size 5. The tag on each line is the weight of the item put in last. The highlighted sizes 1, 2 and 3 are worked out more than once.
- checking
class Solution {
public int unboundedKnapsack(int W, int[] weight, int[] value) {
int best = 0;
for (int i = 0; i < weight.length; i++) {
if (weight[i] <= W) {
// item i goes in last, the rest of the room is solved the same way
best = Math.max(best, value[i] + unboundedKnapsack(W - weight[i], weight, value));
}
}
return best;
}
}How long it takes: each call can branch into all n items and the chain can be W long (with a weight 1 item), so up to O(n^W). Extra room: the call chain, O(W).
Way 2: the clever way
There are only W + 1 bag sizes. Fill a row best[0..W] from small to big. When you reach size c, every smaller size is already known.
best[0] = 0.- For each size
cfrom 1 toW, for each item that fits:best[c] = max(best[c], best[c - weight] + value). - Return
best[W].
Because best[c - weight] is allowed to already contain the same item, repeats come for free.
Fill best[capacity] from 0 to 8
01 / 10
A bag that holds 0 can carry nothing, so best[0] = 0. Every other box is empty for now.
- looking now
- checking
- seen
- done
- answer
- just added
class Solution {
public int unboundedKnapsack(int W, int[] weight, int[] value) {
int[] best = new int[W + 1]; // best[0] = 0
for (int c = 1; c <= W; c++) {
for (int i = 0; i < weight.length; i++) {
if (weight[i] <= c) {
// item i last, plus the best for the room left
best[c] = Math.max(best[c], best[c - weight[i]] + value[i]);
}
}
}
return best[W];
}
}How long it takes: W sizes times n items, O(n * W). Extra room: one row, O(W).
Watch out for
- Greedy by value per weight is wrong, as the
[5, 6]example shows. - If the bag must be filled exactly, start every
best[c]at minus infinity (exceptbest[0] = 0), so impossible sizes stay impossible. - With a huge
W(in the billions) the row does not fit. That is a different problem and needs other tricks.
What interviewers ask next
- Does the loop order matter? For the best value, no: items outside and capacity inside (low to high) gives the same answer. For counting ways, yes. Items outside counts combinations (coin change II), capacity outside counts ordered sequences.
- Print which items to take. Keep
pick[c], the item that gave the best at sizec. Then fromW, takepick[W]and jump toW - weight. - Rod cutting is the same problem: piece lengths are weights, prices are values, the rod length is
W. - Coin change (fewest coins) is the same shape with
minand+1instead ofmaxand+value.
Remember this
Grown-up words: the bag is the knapsack, its size is the capacity W, endless jars mean the unbounded knapsack, the row of best values is 1D dynamic programming, and the 0/1 version is the bounded knapsack.