/ blog

# 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