Knapsack Problem
A thief has a knapsack, but it has a weight capacity w. The sum of the weight of the goods must be under w.
Consider the items: [1..n] with weight [W1..Wn] and value [V1..Vn]
Maximize the dollar value of the stolen goods without tearing the knapsack.
Approach: consider each item and decide to take it or leave it.
Approach
For each item i, take it or leave it.
If you take it, adjust value with Vi and weight with w - Wi. What remains is a subproblem that looks like the original problem but with different values.
recurrence:
maxValue(j, W (capacity limit)) {
0 if W is 0
-inf if W is < 0
0 j == n + 1
max {
Vj + maxValue(j+1, W - wj) <- take it
maxValue(j+1, W) <- leave it
}
}
How to memoize?
Design a table with width and height of n. The first row and column will be all zeros. Each sub problem is looking at one row below and either w or w - Wj. Fill up each row left to right from bottom to top.
Running time: Θ(nW0)
W0 is the actual magnitude, so if W0 = 10,000 then n^5
Called pseudo polynomial, exponential time referenced to the magnitude defined by W.
Rebuilding solution: at each j record if you took or left, then trace back through your table.
def maxSteal_memo(W, weights, values):
# Initialize the tables
T = [0]* (W+1)
S = [-1]* (W+1)
k = len(weights)
assert len(values) == k
for w in range(1, W+1):
opts = [ ( (values[i]+ T[ w - weights[i] ]), i ) for i in range(k) if w - weights[i] >= 0 ]
opts.append( (-float('inf'), -1) ) # In case opts was empty from the previous step.
T[w], S[w] = max(opts)
# This finishes the computation
stolen_item_ids = []
weight_remaining = W
while weight_remaining >= 0:
item_id = S[weight_remaining]
if item_id >= 0:
stolen_item_ids.append('Steal Item ID %d: weight = %d, value = %f' % (item_id, weights[item_id], values[item_id]) )
weight_remaining = weight_remaining - weights[item_id]
else:
break
return T[W], stolen_item_ids