The optimal subset property says that any subset of an optimal solution is also optimal.
For example, suppose that {i1 , i2, …, ik} is the list of indices of the items in an optimal subset with total value V. The sublist {i1 , i2, …, ik-1} has total value V - vik and will fit in a container with capacity C - wik. The optimal subset property says that no other subset of the original set of items excluding item ik that fits in a container with capacity C - wik has value greater than V - vik.
The proof is a simple proof by contradiction. If we were able to find a better subset, we could simply replace items i1 , i2, …, ik-1 in the original solution with that subset to acheive a higher total value than the original solution had without exceeding the capacity of the container. This contradicts the claim that original solution is optimal.
Suppose V[1..n]
is an array of values and W[1..n]
is an array of weights.
The function maxValue(k,c)
computes the maximum value you can carry out in a knapsack with capacity c if you only get to select from items 1..k.
maxValue(k,c) if k == 0 return 0 max = maxValue(k-1,c) if W[k] <= c and maxValue(k-1,c-W[k])+V[k] > max max = maxValue(k-1,c-W[k])+V[k] return max
The basic idea in the recursion is that for each item k in the list of possible items we have to decide whether or not to pack that item. The recursive code looks at both options, don't take the item and do take the item, and determines which of those two options produces the greater value. If you do decide to take the item, you first have to determine whether or not it will fit in your container.
Suppose V[1..n]
is an array of values and W[1..n]
is an array of weights.
The function maxValue(k,c)
computes the maximum value you can carry out in a knapsack with capacity c if you only get to select from items 1..k.
The two dimensional array M[0..n,0..C]
stores the maximum values as we find them. At start, all of the entries of this array are initialized to 0.
maxValue(k,c) if M[k,c] > 0 return M[k,c] if k == 0 return 0 max = maxValue(k-1,c) if W[k] <= c and maxValue(k-1,c-W[k])+V[k] > max max = maxValue(k-1,c-W[k])+V[k] M[k,c] = max return max
The dynamic programming version is a straightforward translation from the memoized version.
maxValue(cap) Let M[0..n,0..cap] be a new array for c = 0 to cap M[0,c] = 0 for row = 1 to n for c = 0 to cap M[row,c] = M[row-1,c] if W[row] <= c and M[row-1,c-W[row]] + V[row] > M[row,c] M[row,c] = M[row-1,c-W[row]] + V[row] return M[n,cap]
There is just one thing we need to be careful about when setting up this code. We need to make sure that at any point where we are using a value from the table we have already filled in that value.
The algorithm handles this problem by filling row 0 of the table first with base cases, and then working down the rows of the table. The code only ever uses values that come from earlier rows, so we are safe.
The dynamic programming solution can tell us the maximum value that we can carry out, but it can not tell us directly which set of items to put in our subset. The fix for this problem is relatively straightforward. We introduce a second two dimensional array T[0..n,0..C]
that stores decisions about whether or not take items. If we are considering whether or not to put item k into a container with maximum capacity c, we store our decision in T[k,c]
.
maxValue(cap) Let M[0..n,0..cap] be a new array Let T[0..n,0..cap] be a new array for c = 0 to cap M[0,c] = 0 T[0,c] = False for row = 1 to n for c = 0 to cap M[row,c] = M[row-1,c] T[row,c] = False if W[row] <= c and M[row-1,c-W[row]] + V[row] > M[row,c] M[row,c] = M[row-1,c-W[row]] + V[row] T[row,c] = True return M[n,cap]
We then use the values stored in the second table to construct and print the optimal solution:
printOptimal(k,c) if c > 0 and k > 0 if T[k,c] == True printOptimal(k-1,c-W[k]) print k + " : " + V[k] else printOptimal(k-1,c)
To print the list of items in the optimal solution we call printOptimal(n,C)
.