knapsack
knapsack
0 j –wi j W
0 0 0 0 0
n 0 goal
FIGURE 8.4 Table for solving the knapsack problem by dynamic programming.
294 Dynamic Programming
capacity j
i 0 1 2 3 4 5
0 0 0 0 0 0 0
w1 = 2, v1 = 12 1 0 0 12 12 12 12
w2 = 1, v2 = 10 2 0 10 12 22 22 22
w3 = 3, v3 = 20 3 0 10 12 22 30 32
w4 = 2, v4 = 15 4 0 10 15 25 30 37
FIGURE 8.5 Example of solving an instance of the knapsack problem by the dynamic
programming algorithm.
The dynamic programming table, filled by applying formulas (8.6) and (8.7),
is shown in Figure 8.5.
Thus, the maximal value is F (4, 5) = $37. We can find the composition of an
optimal subset by backtracing the computations of this entry in the table. Since
F (4, 5) > F (3, 5), item 4 has to be included in an optimal solution along with an
optimal subset for filling 5 − 2 = 3 remaining units of the knapsack capacity. The
value of the latter is F (3, 3). Since F (3, 3) = F (2, 3), item 3 need not be in an
optimal subset. Since F (2, 3) > F (1, 3), item 2 is a part of an optimal selection,
which leaves element F (1, 3 − 1) to specify its remaining composition. Similarly,
since F (1, 2) > F (0, 2), item 1 is the final part of the optimal solution {item 1,
item 2, item 4}.
The time efficiency and space efficiency of this algorithm are both in (nW ).
The time needed to find the composition of an optimal solution is in O(n). You
are asked to prove these assertions in the exercises.
Memory Functions
As we discussed at the beginning of this chapter and illustrated in subsequent
sections, dynamic programming deals with problems whose solutions satisfy a
recurrence relation with overlapping subproblems. The direct top-down approach
to finding a solution to such a recurrence leads to an algorithm that solves common
subproblems more than once and hence is very inefficient (typically, exponential
8.2 The Knapsack Problem and Memory Functions 295
or worse). The classic dynamic programming approach, on the other hand, works
bottom up: it fills a table with solutions to all smaller subproblems, but each of
them is solved only once. An unsatisfying aspect of this approach is that solutions
to some of these smaller subproblems are often not necessary for getting a solution
to the problem given. Since this drawback is not present in the top-down approach,
it is natural to try to combine the strengths of the top-down and bottom-up
approaches. The goal is to get a method that solves only subproblems that are
necessary and does so only once. Such a method exists; it is based on using memory
functions.
This method solves a given problem in the top-down manner but, in addition,
maintains a table of the kind that would have been used by a bottom-up dynamic
programming algorithm. Initially, all the table’s entries are initialized with a spe-
cial “null” symbol to indicate that they have not yet been calculated. Thereafter,
whenever a new value needs to be calculated, the method checks the correspond-
ing entry in the table first: if this entry is not “null,” it is simply retrieved from the
table; otherwise, it is computed by the recursive call whose result is then recorded
in the table.
The following algorithm implements this idea for the knapsack problem. After
initializing the table, the recursive function needs to be called with i = n (the
number of items) and j = W (the knapsack capacity).
ALGORITHM MFKnapsack(i, j )
//Implements the memory function method for the knapsack problem
//Input: A nonnegative integer i indicating the number of the first
// items being considered and a nonnegative integer j indicating
// the knapsack capacity
//Output: The value of an optimal feasible subset of the first i items
//Note: Uses as global variables input arrays W eights[1..n], V alues[1..n],
//and table F [0..n, 0..W ] whose entries are initialized with −1’s except for
//row 0 and column 0 initialized with 0’s
if F [i, j ] < 0
if j < Weights[i]
value ← MFKnapsack(i − 1, j )
else
value ← max(MFKnapsack(i − 1, j ),
Values[i] + MFKnapsack(i − 1, j − Weights[i]))
F [i, j ] ← value
return F [i, j ]
EXAMPLE 2 Let us apply the memory function method to the instance consid-
ered in Example 1. The table in Figure 8.6 gives the results. Only 11 out of 20
nontrivial values (i.e., not those in row 0 or in column 0) have been computed.
296 Dynamic Programming
capacity j
i 0 1 2 3 4 5
0 0 0 0 0 0 0
w1 = 2, v1 = 12 1 0 0 12 12 12 12
w2 = 1, v2 = 10 2 0 — 12 22 — 22
w3 = 3, v3 = 20 3 0 — — 22 — 32
w4 = 2, v4 = 15 4 0 — — — — 37
FIGURE 8.6 Example of solving an instance of the knapsack problem by the memory
function algorithm.
Just one nontrivial entry, V (1, 2), is retrieved rather than being recomputed. For
larger instances, the proportion of such entries can be significantly larger.
Exercises 8.2
1. a. Apply the bottom-up dynamic programming algorithm to the following
instance of the knapsack problem:
b. How many different optimal subsets does the instance of part (a) have?
c. In general, how can we use the table generated by the dynamic program-
ming algorithm to tell whether there is more than one optimal subset for
the knapsack problem’s instance?