6 Dynamic Programming
6 Dynamic Programming
Dynamic Programming
Slides courtesy from Jianhua Ruan, The University of Texas at San Antonio
And some slides from Jeff Edmonds @ York University
5/15/2023 1
In the next few lectures
F(0) = 1;
F(1) = 1;
F(n) = F(n-1) + f(n-2)
Time complexity?
An iterative algorithm
• Problem with the recursive Fib algorithm:
– Each subproblem was solved for many times!
start
goal
G
n
Each edge has a length (cost). We need to get to G from S. Can only move
to the right or bottom. Aim: find a path with the minimum total length
Use a greedy approach
S 3 9 1 2
0
5 3 3 3 3
3 2 5 2
2 3 3 9 3
2 4 2 3
6 2 3 7 4
3 6 3 3
4 6 3 1 3
1 2 3 2
Recursive solution
• Suppose we’ve found the shortest path n
• It must use one of the two edges:
– (m, n-1) to (m, n) Case 1
– (m-1, n) to (m, n) Case 2
• If case 1
– find shortest path from (0, 0) to (m, n-1)
– SP(0, 0, m, n-1) + dist(m, n-1, m, n) is the
m
overall shortest path
• If case 2
– find shortest path from (0, 0) to (m-1, n)
– SP(0, 0, m-1, n) + dist(m-1, n, m, n) is the overall shortest path
• We don’t know which case is true
– But if we’ve found the two paths, we can compare
– Actual shortest path is the one with shorter overall length
Recursive solution
Let F(i, j) = SP(0, 0, i, j). => F(m, n) is length of SP from (0, 0) to (m, n)
n
F(m-1, n) + dist(m-1, n, m, n)
F(m, n) = min
F(m, n-1) + dist(m, n-1, m, n)
Generalize
F(i-1, j) + dist(i-1, j, i, j)
m
F(i, j) = min
F(i, j-1) + dist(i, j-1, i, j)
• To find shortest path from
(0, 0) to (m, n), we need to
find shortest path to both (m-
1, n) and (m, n-1)
m • If we use recursive call, some
subpaths will be recomputed
for many times
• Strategy: solve subproblems in
certain order such that
redundant computation can
n be avoided
Dynamic Programming
Let F(i, j) = SP(0, 0, i, j). => F(m, n) is length of SP from (0, 0) to (m, n)
n
F(i-1, j) + dist(i-1, j, i, j)
F(i, j) = min
F(i, j-1) + dist(i, j-1, i, j)
m
i = 1 .. m, j = 1 .. n
Boundary condition: i = 0 or j = 0.
Easy to figure out. Number of unique subproblems =
m*n
(i-1, j)
Data dependency determines
order to compute: left to right, top
(i, j-1)
to bottom
(i, j)
Dynamic programming illustration
S 3 9 1 2
0
5 3 3 3 3
3 2 5 2
2 3 3 9 3
2 4 2 3
6 2 3 7 4
3 6 3 3
4 6 3 1 3
1 2 3 2
F(i-1, j) + dist(i-1, j, i, j)
F(i, j) = min
F(i, j-1) + dist(i, j-1, i, j)
Dynamic programming illustration
S 3 9 1 2
0 3 12 13 15
5 3 3 3 3
3 2 5 2
5 6 8 13 15
2 3 3 9 3
2 4 2 3
7 9 11 13 16
6 2 3 7 4
3 6 3 3
13 11 14 17 20
4 6 3 1 3
1 2 3 2
17 17 17 18 20
G
F(i-1, j) + dist(i-1, j, i, j)
F(i, j) = min
F(i, j-1) + dist(i, j-1, i, j)
Trace back
S 3 9 1 2
0 3 12 13 15
5 3 3 3 3
3 2 5 2
5 6 8 13 15
2 3 3 9 3
2 4 2 3
7 9 11 13 16
6 2 3 7 4
3 6 3 3
13 11 14 17 20
4 6 3 1 3
1 2 3 2
17 17 17 18 20
G
Three versions:
0-1 knapsack problem: take each
item or leave it
Fractional knapsack problem:
items are divisible
Unbounded knapsack problem:
unlimited supplies of each item.
Which one is easiest to solve?
wn wn
Find an optimal solution using items Find an optimal solution using items
1, 2, …, n-1 with weight limit W - wn 1, 2, …, n-1 with weight limit W
Dynamic Approach for Knapsack
• We need to derive a recurrence relation that
expresses a solution to an instance of the problem
in terms of solutions to its smaller subinstances.
• Consider an instance defined by the first i items
1<=i<=n
– with weights w1, …, wi
– values v1, …, vi
– capacity j, 1<=j<=W
– V[i, j] be the value of an optimal solution to this
instance the most suitable subset of first i items that fit
into the knapsack of capacity j.
Dynamic Approach for Knapsack
• We can divide all subsets of the first i items that
fit the knapsack of capacity w into two categories
– 1. Among the subsets that do not include the ith item,
the value of an optimal subset is, V[ i-1, w]
– 2. Among the subsets that do include the ith item, an
optimal subset is made up of this item and an optimal
subset of the first i-1 items that fit into the knapsack
of capacity w-wi (w-wi>=0)
– The value of such an optimal subset is vi+ V[i-1, w-wi]
Recursive formulation
Generalize
1 2 2 0
2 4 3 0 wi
4 5 6 0 V[i, w]
5 2 4 0
6 6 9 0
1 2 2 0
2 4 3 0
3 3 3 0
4 5 6 0
5 2 4 0
6 6 9 0
1 2 2 0 0 2 2 2 2 2 2 2 2 2
2 4 3 0 0 2 2 3 3 5 5 5 5 5
3 3 3 0 0 2 3 3 5 5 6 6 8 8
4 5 6 0 0 2 3 3 6 6 8 9 9 11
5 2 4 0 0 4 4 6 7 7 10 10 12 13
6 6 9 0 0 4 4 6 7 9 10 13 13 15
1 2 2 0 0 2 2 2 2 2 2 2 2 2
2 4 3 0 0 2 2 3 3 5 5 5 5 5
3 3 3 0 0 2 3 3 5 5 6 6 8 8
4 5 6 0 0 2 3 3 6 6 8 9 9 11
5 2 4 0 0 4 4 6 7 7 10 10 12 13
6 6 9 0 0 4 4 6 7 9 10 13 13 15
Optimal value: 15
Item: 6, 5, 1
Weight: 6 + 2 + 2 = 10
Value: 9 + 4 + 2 = 15
Binary Knapsack
Time complexity
• Θ (nW)
• Polynomial?
– Pseudo-polynomial
– Works well if W is small
• Consider following items (weight, value):
(10, 5), (15, 6), (20, 5), (18, 6)
• Weight limit 35 Events scheduling problem
– Optimal solution: item 2, 4 (value = 12). Iterate: 2^4 = 16
subsets
– Dynamic programming: fill up a 4 x 35 = 140 table entries
• What’s the problem?
– Many entries are unused: no such weight combination
– Top-down may be better
Sequence alignment
• Compare two strings to see if they are similar
– We need to define similarity
– Very useful in many applications
– Comparing DNA sequences, articles, source code, etc.
x: A B C B D A B
BCBA =
LCS(x, y)
y: B D C A B A
functional notation,
but not a function
Brute-force LCS algorithm
Check every subsequence of x[1 . . m] to see
if it is also a subsequence of y[1 . . n].
Analysis
• 2m subsequences of x (each bit-vector of length m
determines a distinct subsequence of x).
• Hence, the runtime would be exponential !
z
n
y
j
m
x
n
y
m
x
n
y
m
x
n
y
1 2 i m
x: ...
1 2 j n
y: ...
Recursive algorithm for LCS
LCS(x, y, i, j)
if x[i] = y[ j]
then c[i, j] LCS(x, y, i–1, j–1) + 1
else c[i, j] max{ LCS(x, y, i–1, j),
LCS(x, y, i, j–1)}
Worst-case: x[i] y[ j], in which case the
algorithm evaluates two subproblems, each
with only one parameter decremented.
Recursion tree
m = 3, n = 4: 3,4
0 j n
i C(i, j)
m
DP Algorithm
LCS-Length(X, Y)
1. m = length(X) // get the # of symbols in X
2. n = length(Y) // get the # of symbols in Y
3. for i = 1 to m c[i,0] = 0 // special case: Y[0]
4. for j = 1 to n c[0,j] = 0 // special case: X[0]
5. for i = 1 to m // for all X[i]
6. for j = 1 to n // for all Y[j]
7. if ( X[i] == Y[j])
8. c[i,j] = c[i-1,j-1] + 1
9. else c[i,j] = max( c[i-1,j], c[i,j-1] )
10. return c
LCS Example
We’ll see how LCS algorithm works on the following
example:
• X = ABCB
• Y = BDCAB
LCS(X, Y) = BCB
X =AB C B
Y= B DCAB
LCS Example (0) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
A
1
2 B
3 C
4 B
X = ABCB; m = |X| = 4
Y = BDCAB; n = |Y| = 5
Allocate array c[5,6]
LCS Example (1) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
0 0 0 0 0 0
A
1 0
2 B
0
3 C 0
4 B 0
for i = 1 to m c[i,0] = 0
for j = 1 to n c[0,j] = 0
LCS Example (2) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
0 0 0 0 0 0
A
1 0 0
2 B
0
3 C 0
4 B 0
if ( Xi == Yj )
c[i,j] = c[i-1,j-1] + 1
else c[i,j] = max( c[i-1,j], c[i,j-1] )
LCS Example (3) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
0 0 0 0 0 0
A
1 0 0 0 0
2 B
0
3 C 0
4 B 0
if ( Xi == Yj )
c[i,j] = c[i-1,j-1] + 1
else c[i,j] = max( c[i-1,j], c[i,j-1] )
LCS Example (4) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
0 0 0 0 0 0
A
1 0 0 0 0 1
2 B
0
3 C 0
4 B 0
if ( Xi == Yj )
c[i,j] = c[i-1,j-1] + 1
else c[i,j] = max( c[i-1,j], c[i,j-1] )
LCS Example (5) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
0 0 0 0 0 0
A
1 0 0 0 0 1 1
2 B
0
3 C 0
4 B 0
if ( Xi == Yj )
c[i,j] = c[i-1,j-1] + 1
else c[i,j] = max( c[i-1,j], c[i,j-1] )
LCS Example (6) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
0 0 0 0 0 0
A
1 0 0 0 0 1 1
2 B
0 1
3 C 0
4 B 0
if ( Xi == Yj )
c[i,j] = c[i-1,j-1] + 1
else c[i,j] = max( c[i-1,j], c[i,j-1] )
LCS Example (7) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
0 0 0 0 0 0
A
1 0 0 0 0 1 1
2 B
0 1 1 1 1
3 C 0
4 B 0
if ( Xi == Yj )
c[i,j] = c[i-1,j-1] + 1
else c[i,j] = max( c[i-1,j], c[i,j-1] )
LCS Example (8) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
0 0 0 0 0 0
A
1 0 0 0 0 1 1
2 B
0 1 1 1 1 2
3 C 0
4 B 0
if ( Xi == Yj )
c[i,j] = c[i-1,j-1] + 1
else c[i,j] = max( c[i-1,j], c[i,j-1] )
LCS Example (9) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
0 0 0 0 0 0
A
1 0 0 0 0 1 1
2 B
0 1 1 1 1 2
3 C 0 1 1
4 B 0
if ( Xi == Yj )
c[i,j] = c[i-1,j-1] + 1
else c[i,j] = max( c[i-1,j], c[i,j-1] )
LCS Example (10) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
0 0 0 0 0 0
A
1 0 0 0 0 1 1
2 B
0 1 1 1 1 2
3 C 0 1 1 2
4 B 0
if ( Xi == Yj )
c[i,j] = c[i-1,j-1] + 1
else c[i,j] = max( c[i-1,j], c[i,j-1] )
LCS Example (11) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
0 0 0 0 0 0
A
1 0 0 0 0 1 1
2 B
0 1 1 1 1 2
3 C 0 1 1 2 2 2
4 B 0
if ( Xi == Yj )
c[i,j] = c[i-1,j-1] + 1
else c[i,j] = max( c[i-1,j], c[i,j-1] )
LCS Example (12) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
0 0 0 0 0 0
A
1 0 0 0 0 1 1
2 B
0 1 1 1 1 2
3 C 0 1 1 2 2 2
4 B 0 1
if ( Xi == Yj )
c[i,j] = c[i-1,j-1] + 1
else c[i,j] = max( c[i-1,j], c[i,j-1] )
LCS Example (13) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
0 0 0 0 0 0
A
1 0 0 0 0 1 1
2 B
0 1 1 1 1 2
3 C 0 1 1 2 2 2
4 B 0 1 1 2 2
if ( Xi == Yj )
c[i,j] = c[i-1,j-1] + 1
else c[i,j] = max( c[i-1,j], c[i,j-1] )
LCS Example (14) ABCB
j 0 1 2 3 4 5
BDCAB
i Y[j] B D C A B
0 X[i]
0 0 0 0 0 0
A
1 0 0 0 0 1 1
2 B
0 1 1 1 1 2
3 C 0 1 1 2 2 2
4 B 0 1 1 2 2 3
if ( Xi == Yj )
c[i,j] = c[i-1,j-1] + 1
else c[i,j] = max( c[i-1,j], c[i,j-1] )
LCS Algorithm Running Time
O(m*n)
since each c[i,j] is calculated in
constant time, and there are m*n
elements in the array
How to find actual LCS
• The algorithm just found the length of LCS, but not LCS itself.
• How to find the actual LCS?
• For each c[i,j] we know how it was acquired:
c[i 1, j 1] 1 if x[i] y[ j ],
c[i, j ]
max( c[i, j 1], c[i 1, j ]) otherwise
• A match happens only when the first equation is taken
• So we can start from c[m,n] and go backwards, remember
x[i] whenever c[i,j] = c[i-1, j-1]+1.
2 B
0 1 1 1 1 2
3 C 0 1 1 2 2 2
4 B 0 1 1 2 2 3
2 B
0 1 1 1 1 2
3 C 0 1 1 2 2 2
4 B 0 1 1 2 2 3
LCS (reversed order): B C B
LCS (straight order): B C B
(this string turned out to be a palindrome)
A more general problem
• Aligning two strings, such that
Match = 1
Mismatch = 0 (or other scores)
Insertion/deletion = -1
• Aligning ABBC with CABC
– LCS = 3: ABC
– Best alignment
ABBC ABBC
CABC CABC
Score = 2 Score = 1
Aligment Problem
• Let F(i, j) be the best alignment score between
X[1..i] and Y[1..j].
• F(m, n) is the best alignment score between X and Y
• Recurrence
2 B
3 B
4 C
X = ABBC; m = |X| = 4
Y = CABC; n = |Y| = 4
Allocate array F[5,5]
Alignment Example ABBC
CABC
j 0 1 2 3 4
i Y[j] C A B C
X[i]
0 0 -1 -2 -3 -4
A -1 0 0 -1 -2
1
2 B -2 -1 0 1 0
3 B -3 -2 -1 1 1
4 C -4 -2 -2 0 2
2 B -2 -1 0 1 0
ABBC
3 B -3 -2 -1 1 1 CABC
Score = 2
4 C -4 -2 -2 0 2
C A B C
-1
1 0
Alignment as a longest path problem
C A B C
-1
1 0