Dynamic Programming Exercises Solution
Dynamic Programming Exercises Solution
1. (a) Solve the following instance of the {0, 1} Knapsack Problem with
four items where the maximum allowed weight is Wmax = 10.
i
1
2
3
4
bi 25 15 20 36
wi
7
2
3
6
Solution: We proceed with this solution as outlined in the notes.
We define B[k, w] to be the optimal solution that can be obtained
using only the first k items, with maximum allowed total weight of
w. Here k ranges from 1 to 4, while w ranges from 0 to 10. We also
define B[0, w] = 0 for all w.
Then, as we did in class, we have to fill in the table, one row at a time,
to find the optimal solution, using the recurrence type of equation
that can be found in the class notes. This table will end up having
these values:
0 1 2
3
4
5
6
7
8
9 10
1 0 0 0
0
0
0
0 25 25 25 25
2 0 0 15 15 15 15 15 25 25 40 40
3 0 0 15 20 20 35 35 35 35 40 45
4 0 0 15 20 20 35 36 36 51 56 56
So the maximum benefit obtainable is 56. Since this example is small,
we can easily figure out that we want to take items 3 and 4 to obtain
this benefit. Otherwise, as also mentioned in class, we can trace
back through the table to find this out.
(b) For comparison, what is the solution to the corresponding Fractional
Knapsack problem with the same value of Wmax (use the greedy
method we discussed for this Fractional version)?
Solution: Recall that we compute the value indices and take items
in order of the value indices to fill up the knapsack. So I have added
another line to the table below for the value indices.
i
1
2
3
4
bi 25 15 20 36
wi
7
2
3
6
vi 3 47 7 12 6 23
6
So we select items in the order 2,3,4,1.
Doing so will lead us to the Fractional Knapsack solution consisting of
items 2, 3, and 56 of item 4, for a total benefit of 15 + 20 + 65 (36) = 65.
2. Do the same as above (find solutions to both the {0, 1} and Fractional
Knapsack problems) for this collection of items, where Wmax = 11.
i
bi
wi
1
14
4
2
12
3
3
15
8
4
20
7
5
16
3
would get
5
6
14 14
14 14
14 14
14 14
16 28
for
7
14
26
26
26
30
the {0, 1}
8
9
14 14
26 26
26 26
26 26
30 30
From this table, you can see that the maximum benefit for the {0, 1}
Knapsack Problem for this set of items is 42. What is the set of items
that achieves this benefit? You can find that by tracing backwards through
the table. Since B[5, 11] > B[4, 11] we see that item 5 must be used in the
solution.
Then we want to compare B[4, 8] and B[3, 8]. Since they are the same, we
conclude that item 4 is not used in the best solution. Continuing in this
way, we can determine that the best solution uses items 5, 2, and 1.
For the Fractional case, we again compute the value indices.
i
bi
wi
vi
1
14
4
3 23
2
12
3
4
3
15
8
1 78
4
20
7
2 67
5
16
3
5 13
S[1]
S[2]
S[3]
S[4]
S[5]
S[6]
S[7]
S[8]
S[9]
S[10]
1.
and
These cases above should be considered our base cases and we then work
from these to compute values of S[n] for higher values of n.
The main idea, then is to consider what happens if we use a stamp of a
particular value. For example, if we want postage of n pence, then we
can clearly get it by taking one stamp of 1p, and S[n 1] stamps (of
appropriate values) to make up the remaining postage of (n 1)p. Or if
we take a 7p stamp, then we need S[n 7] stamps (of the right values)
to make up the rest, and similarly if we take a 10p stamp. This idea is
essentially the heart of the dynamic programming algorithm that we use.
The pseudo-code is given below:
Stamps(n)
We assume here that n 0.
This procedure will determine the minimum number of stamps
of values 1p, 7p, and 10p to make up total postage of n pence.
Base cases
1 Set up the array S starting as above (with the
values S[i] for i = 0, . . . , 10).
2 if n 10 then
3
return S[n]
4
else
5
for j 11 to n do
6
S[j] = 1 + min{S[j 1], S[j 7], S[j 10]}
7 return S[n]
As you can see, there is one for loop in the body of the pseudo-code,
and this dominates the running time of the algorithm. Thus, finding S[N ]
takes time which is in O(N ).
Now we can find S[n] easily as above, but knowing this doesnt tell us the
exact nature of the stamps we need. Right? It would be nice to know
that we can make 45p postage with six stamps, and that we need one 1p,
two 7p, and three 10p stamps to do so. (For some values of n there could
certainly be more than one way to do this. For example, we can get 63p
with nine 7p stamps, or with six 10p and three 1p stamps.)
Well, we can easily do this too with another array called, say, P . Then P [n]
will be a vector of length 3 that will tell us what stamps we need to make
postage for np. For example, we would have P [45] = (1, 2, 3), meaning
that we need one 1p, two 7p, and three 10p stamps. In general, if we have
P [n] = (a, b, c), then we take a 1p stamps, b 7p stamps, and c 10p stamps
to make np postage. So, for example P [7] = (0, 1, 0), P [10] = (0, 0, 1), and
P [25] = (1, 2, 1). (As I said earlier, P [n] isnt really uniquely defined, but
thats not important for us. We just want some way, using the minimum
number of stamps, to get n pence.)
The calculation for S[n] doesnt change. All we do is determine which
denomination of postage we use and add it to the appropriate value of
P [n 1], P [n 7], or P [n 10]. The modified pseudo-code is given below.
The addition of vectors in lines 9, 11, and 13 is done component-wise in
the usual way, e.g. (3, 4, 1) + (0, 1, 0) = (3, 5, 1). The running time is still
O(n).
Stamps(n)
We assume here that n 0.
This procedure will determine the minimum number of stamps of values
1p, 7p, and 10p to make up total postage of n pence. We also return
a vector, P [n] which will tell us how many of each stamp is necessary.
Base cases
1 Set up the array S starting as above (with the
values S[i] for i = 0, . . . , 10).
2 Also set up an array P [i] for i = 0, . . . , 10. That is P [0] = (0, 0, 0),
P [1] = (1, 0, 0), P [2] = (2, 0, 0), . . . , P [7] = (0, 1, 0), etc.
3
4
5
6
7
8
9
10
11
12
13
14
if n 10 then
return S[n] and P [n]
else
for j 11 to n do
S[j] = 1 + min{S[j 1], S[j 7], S[j 10]}
Now set P [j] based on the value of S[j].
In other words, which stamp did we use?
if S[j] == (1 + S[j 1]) then
P [j] P [j 1] + (1, 0, 0)
elseif S[j] == (1 + S[j 7]) then
P [j] P [j 7] + (0, 1, 0)
else
P [j] P [j 10] + (0, 0, 1)
return S[n] and P [n]
4. (This is also from the initial problem sheet that I gave to you.)
American coins come in five different values, namely 50-cent, 25-cent, 10cent, 5-cent, and 1-cent coins. Therefore, if we wanted change for 11 cents
(or 11), we can do this with one 10 coin and one 1 coin, or two 5 coins
and one 1, or one 5 and 6 1 coins, or with eleven 1 coins. So there
are four ways to give change for 11.
How many ways are there to make change for N ? (And how can you
compute this number efficiently?)
We consider the coins in order 1, 5, 10, 25, 50. We compute the number
of ways of making change, first using only 1 coins, then using both 1
4
and 5 coins, and so forth. Doing the computations in this order, were
using the previously computed information to save us some work (in the
form of repeated computations).
So if you want, we can consider defining a 2-dimensional array (table) of
the form A[i, n] for i = 1, . . . , 5 and n = 1 . . . , N . The index i = 3, for
example, tells us that were using the first three coins to make change (i.e.
the 1, 5, and 10 coins). Obviously the value n is the amount for which
were making change. We want to find A[5, N ].
First of all, we note that A[1, n] = 1 for all n N . Why? Theres only
one way to make change for nusing only pennies. How many ways are
there to make change using 1 and 5 coins? We can find this using the
following bit of pseudo-code.
1
2
for j 0 to N Do
A[2, j] = bj/5c + 1
Verify that this number is correct? (Hint: The +1 comes from the fact
that you can use no 5 coins.)
Then we can find the values of A[3, n] in this fashion
1
2
3
4
5
for j 0 to N Do
A[3, j] = A[2, j]
for k 10 to N by 10 Do
(Increment counter by 10.)
if (j k >= 0) Then
A[3, j] = A[3, j] + A[2, j k]
Because this pseudo-code has two for loops in it, you can see that it
runs in time O(N 2 ). You should also verify that it correctly computes the
number of ways of giving change using 1, 5 and 10 coins.
Adding on 25 and 50 coins is similar to the above pseudo-code, we just
increment the counter in the second for loop by 25 or 50 as appropriate
(and use the appropriate index i in the values of A[i, n]).
Overall, this will let us compute the value of A[5, N ], the value we want,
in time O(N 2 ).
Tardos.)
5. (Adapted from Algorithm Design by Jon Kleinberg & Eva
The current class of students at Sandhurst has an annual picnic (ok, maybe
they dont really do this, but for the purposes of this question they do...).
This year it happens that the picnic has fallen on a rainy day and the
ranking officer decides to postpone the picnic and must notify everyone
by phone. Here is the mechanism for doing this.
Each person except for the ranking officer reports to a unique superior
officer. Thus, the reporting hierarchy can be described as a tree T , rooted
at the ranking officer, in which each other node v has a parent node u
equal to his or her superior officer. Conversely, we can call v a direct
subordinate of u. See the picture below, where A is the ranking officer, B
and D are direct subordinates of A, and C is a direct subordinate of B.
5
Am
J
J^
J
m
Bm
D
?
Cm
To notify everyone of the postponement, the ranking officer first calls each
of her direct subordinates, one at a time. As soon as each subordinate gets
the phone call, he or she must notify each of his or her direct subordinates,
one at a time. The process continues in this was until everyone is notified.
Note that each person in this process will only call direct subordinates,
for example A would not be allowed to call C.
We can picture this notification process as divided into rounds. In one
round, each person who has already learned of the postponement can call
one of his or her subordinates on the phone. The number of rounds it takes
for everyone to be notified might depend upon the sequence in which each
person calls their direct subordinates. In the given example, it will take
only two rounds if A starts by calling B, but will take three rounds if A
starts by calling D.
Give an efficient algorithm that determines the minimum number of rounds
needed for everyone to be notified for a rooted tree T as described above.
How can you output a sequence of phone calls (i.e. a schedule for each
person) that achieves this minimum number of rounds?
Solution: The recursive type of problems we need to consider naturally
come from looking at a node and all of the descendants of that node. In
the example above, B has one child, so needs one round to inform its
children. If B had three children, then it would need three rounds to
inform all of its children.
In general, a vertex wants to inform its children in a good order in
which to minimize the time needed to inform all of the descendants of
itself. Nodes just above leaves will need a number of rounds equal to their
children, but vertices with larger heights (closer to the root) will need
even more than the number of children, as you need to consider how long
is needed to inform the entire subtree rooted at a vertex. You need to
consider more complicated trees in your solution method. (Unfortunately,
the small tree that Ive drawn here might be slightly misleading.)
Let us use a label called r(v) for a vertex v. (Im using r as Im thinking
that the value tells me how many rounds are necessary until v and all of
its descendants have been informed of the news.)
A leaf has label r(v) = 0 (if that vertex knows the news, then you need
zero rounds until it (and all of its non-existent children) are informed of
the news). Nodes just above leaves will have a label
r(v) = number of children of v.
Now assume that we have (recursively) calculated values of r(w) for each
child of an internal vertex v, and we want to find r(v). (As above, we
6
know how to find r(v) for vertices with height 1, so we need to consider
vertices with height 2 and larger.)
How do we find r(v)? We want to inform the children of v in a proper
order so that we minimize the value of r(v). Intuitively (I hope), it seems
that we want to inform the child, w, of v having largest value of r(w), so
that w learns the message first, and then can begin to inform its children
as early as possible (while v continues to inform the remaining children,
keeping in mind that this process is parallel in the sense that each vertex
that knows the message can inform one of its children every round).
So, with this in mind, suppose that v has k children, which I will call
w1 , w2 , . . . , wk , and further suppose that I have labeled the children of v
so that
r(w1 ) r(w2 ) r(wk ).
(1)
(Note that we can have each child wi inform its parent of the value of
r(wi ) when it has completed computing this value, and then v can sort
the r(wi ) values once it knows them all.)
Then, v will inform its children in the order w1 , w2 , . . . , wk . So, child wi
will be informed on round i after v becomes informed of the message.
Then, the subtree rooted at vertex wi requires r(wi ) additional rounds
until all vertices in that subtree are informed.
Therefore, I claim that
r(v) = max {1 + r(w1 ), 2 + r(w2 ), 3 + r(w3 ), . . . , k + r(wk )} .
(2)
Will Equation (2) actually find the minimum possible value of r(v)?
To see that it does, we assume that the values of r(w1 ), . . . , r(wk ) have
been correctly computed (recursively). (This should be clear for leaves
and for vertices of height 1.) Also, assume that the children of v have
been labeled in a manner so that Equation (1) holds.
Then let i {1, . . . , k} be a value so that r(v) = i + r(wi ) holds (i.e. wi is
a child that defines the value of r(v)). What would happen if we were to
swap the value of r(wi ) with some other value r(wj ) for i 6= j? (In other
words, could I somehow lower the value of r(v) by rearranging the order
in which the children of v are informed?)
If we take j < i, then let us compare the two values i + r(wj ) and i + r(wi )
(since vertex wj is now being informed in round i, and r(wj ) rounds are
needed to inform all descendants of wj ). By the ordering of the r values
of the descendants of v (Equation (1)), we have
r(wj ) r(wi )
hence,
i + r(wj ) i + r(wi ).
Therefore, the value of r(v) wouldnt decrease through such a swap of the
vertices wi and wj .
Suppose, instead, that we swap vertices wi and wj for some j > i? Then,
let us consider the two values i + r(wi ) and j + r(wi ). (Now, vertex wi is
now being informed in round j, and r(wi ) rounds are required to inform
all the descendants of wi .)
7
sj
0
2
9
7
fj
6
10
15
18
vj
2
4
6
7
p(j)
0
0
1
1
Solve this instance of the weighted interval scheduling problem, i.e. find a
set of (non-conflicting) intervals with maximum total weight.
Solution: Note that the intervals are already sorted by their finish time,
and I have added the value p(j) for each interval. Recall that the value
p(j) is the largest index of an interval which is compatible with interval
j, i.e. interval p(j) could be scheduled together with interval j, but not
interval p(j)+1 as that would conflict with interval j. (Of course, p(j) = 0
if there is no such interval.)
Now recall that Opt(j) was defined to be the best solution that is obtainable using intervals 1, . . . , j, where we define Opt(0) = 0. Then we have
that
Opt(j) = max {Opt(j 1), vj + Opt(p(j))} .
Therefore, we have
Opt(0)
Opt(1)
Opt(2)
Opt(3)
Opt(4)
sj
1
2
3
5
7
12
9
15
fj
4
6
9
10
14
16
18
20
vj
4
2
1
6
3
1
5
3
p(j)
0
0
0
1
2
4
3
5
Solution: Again, I have added in the values of p(j) for each interval in
the table, as the tasks are already sorted by their finishing times.
In this case we have these values:
Opt(0)
Opt(1)
Opt(2)
Opt(3)
Opt(4)
Opt(5)
Opt(6)
Opt(7)
Opt(8)
10
Why? If we use location xj , then we combine this with the best solution
from sites x1 , x2 , . . . , xe(j) . Otherwise if we dont use xj then we take the
best solution from sites x1 , x2 , . . . , xj1 . Since were trying to maximize
our revenue, we take the better (maximum) of those two options.
9. Longest Increasing Subsequence. (This is a classical dynamic programming problem.) Given a sequence of real numbers A1 , A2 , . . . , An , determine a subsequence (not necessarily contiguous) of maximum length in
which the values form a strictly increasing sequence.
For example, given the sequence of integers
3, 4, 14, 0, 1, 3, 5, 2, 5, 10
the subsequence consisting of 4, 5, 10 is an increasing subsequence, as is
3, 0, 3, 5, 10. Your task is to find a longest subsequence so that the values
are strictly increasing. (There might be repeated numbers in the original
sequence, but there can be no repeats in a longest subsequence since it
must be strictly increasing.)
Hint: Define L(j) to be the length of the longest increasing subsequence
that ends at (and includes) position j. How can you find L(j) in terms
of the values L(i) for i < j? Once you know the L(j) values, whats the
answer to the original problem? And how long does it take to compute
all of the L(j) values?
Solution: As suggested above, we define L(j) to be the length of longest
increasing subsequence that ends at (and includes) position j. If we are
able to compute these L(j) values, then the solution to our problem is
equal to L = max{L(1), L(2), . . . , L(n)}. Why? Because the longest
increasing subsequence must end at (and include) one of the positions
1, 2, . . . , n.
We note that L(1) = 1, since the single value A1 is an increasing subsequence. Also, L(2) = 1 or 2, depending upon whether A1 A2 or
A1 < A2 (recall that the sequence must be strictly increasing).
If we have computed L(1), L(2), . . . , L(i) for some i, how do we use these
values to find L(i + 1)? We compute
L(i+1) = 1+max{L(j) : Aj < Ai+1 }, or possibly L(i+1) = 1 (see below).
In other words, check all Aj , find those that are less than Ai+1 . We do this
because if Aj < Ai+1 , then we can increase the longest subsequence that
ends at Aj by one more value (namely, adding Ai+1 to that subsequence).
Note that if there are no values Aj that are less than Ai+1 , then we define
L(i + 1) = 1 (as for L(1)).
Computing L(i + 1) requires us to check all of the values A1 , . . . , Ai , i.e.
O(i) time. So, in total, computing all of the L(i) values takes O(1+2+3+
+n) = O(n2 ) time. Then finding the value L = max{L(1), L(2), . . . , L(n)}
takes O(n) time. So finding the value L takes O(n2 ) time.
10. Balanced Partition. Suppose that you have a set, A = {a1 , . . . , an }, of n
integers, each of which is between 0 and K (inclusive). Your goal is to find
11
12