Dynamic Programming: CSE 431/531: Algorithm Analysis and Design (Spring 2022)
Dynamic Programming: CSE 431/531: Algorithm Analysis and Design (Spring 2022)
Dynamic Programming: CSE 431/531: Algorithm Analysis and Design (Spring 2022)
Dynamic Programming
Lecturer: Shi Li
Department of Computer Science and Engineering
University at Buffalo
Paradigms for Designing Algorithms
Greedy algorithm
Make a greedy choice
Prove that the greedy choice is safe
Reduce the problem to a sub-problem and solve it iteratively
Usually for optimization problems
Divide-and-conquer
Break a problem into many independent sub-problems
Solve each sub-problem separately
Combine solutions for sub-problems to form a solution for the
original one
Usually used to design more efficient algorithms
2/79
Paradigms for Designing Algorithms
Dynamic Programming
Break up a problem into many overlapping sub-problems
Build solutions for larger and larger sub-problems
Use a table to store solutions for sub-problems for reuse
3/79
Recall: Computing the n-th Fibonacci Number
F0 = 0, F1 = 1
Fn = Fn−1 + Fn−2 , ∀n ≥ 2
Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, · · ·
Fib(n)
1: F [0] ← 0
2: F [1] ← 1
3: for i ← 2 to n do
4: F [i] ← F [i − 1] + F [i − 2]
5: return F [n]
4/79
Outline
5/79
Recall: Interval Schduling
Input: n jobs, job i with start time si and finish time fi
each job has a weight (or value) vi > 0
i and j are compatible if [si , fi ) and [sj , fj ) are disjoint
Output: a maximum-size subset of mutually compatible jobs
0 1 2 3 4 5 6 7 8 9
100 50 30
25 50
90 80
80 70
6/79
Hard to Design a Greedy Algorithm
Job with the earliest finish time? No, we are ignoring weights
Job with the largest weight? No, we are ignoring times
weight
Job with the largest ?
length
No, when weights are equal, this is the shortest job
0 1 2 3 4 5 6 7 8 9
7/79
Designing a Dynamic Programming Algorithm
0 1 2 3 4 5 6 7 8 9
i opt[i]
2 100 5 50 9 30
0 0
4 25 8 50
1 80
3 90 7 80
2 100
1 80 6 70
3 100
4 105
5 150
Sort jobs according to non-decreasing order 6 170
of finish times 7 185
opt[i]: optimal value for instance only 8 220
containing jobs {1, 2, · · · , i} 9 220
8/79
Designing a Dynamic Programming Algorithm
Focus on instance
0 1 2 3 4 5 6 7 8 9
{1, 2, 3, · · · , i},
2 100 5 50 9 30
4 25 8 50
opt[i]: optimal value for the
3 90 7 80
instance
1 80 6 70 assume we have computed
opt[0], opt[1], · · · , opt[i − 1]
A: opt[i − 1]
A: opt[i − 1]
10/79
Designing a Dynamic Programming Algorithm
0 1 2 3 4 5 6 7 8 9
2 100 5 50 9 30
4 25 8 50
3 90 7 80
1 80 6 70
opt[0] = 0
opt[1] = max{opt[0], 80 + opt[0]} = 80
opt[2] = max{opt[1], 100 + opt[0]} = 100
opt[3] = max{opt[2], 90 + opt[0]} = 100
opt[4] = max{opt[3], 25 + opt[1]} = 105
opt[5] = max{opt[4], 50 + opt[3]} = 150 11/79
Designing a Dynamic Programming Algorithm
0 1 2 3 4 5 6 7 8 9
2 100 5 50 9 30
4 25 8 50
3 90 7 80
1 80 6 70
13/79
How Can We Recover the Optimum Schedule?
14/79
Recovering Optimum Schedule: Example
i opt[i] b[i]
0 0 ⊥
1 80 Y
0 1 2 3 4 5 6 7 8 9
2 100 Y
3 100 N 2 100 5 50 9 30
4 105 Y 4 25 8 50
5 150 Y i 3 90 7 80
6 170 Y
1 80 6 70
7 185 Y
8 220 Y
9 220 N
15/79
Dynamic Programming
16/79
Outline
17/79
Subset Sum Problem
Input: an integer bound W > 0
a set of n items, each with an integer weight wi > 0
Output: a subset S of items that
X X
maximizes wi s.t. wi ≤ W.
i∈S i∈S
Example:
W = 35, n = 5, w = (14, 9, 17, 10, 13)
Optimum: S = {1, 2, 4} and 14 + 9 + 10 = 33
18/79
Greedy Algorithms for Subset Sum
Candidate Algorithm:
Sort according to non-increasing order of weights
Select items in the order as long as the total weight remains below
W
19/79
Design a Dynamic Programming Algorithm
A: opt[i − 1, W 0 ]
A: opt[i − 1, W 0 − wi ] + wi
20/79
Dynamic Programming
21/79
Dynamic Programming
1: for W 0 ← 0 to W do
2: opt[0, W 0 ] ← 0
3: for i ← 1 to n do
4: for W 0 ← 0 to W do
5: opt[i, W 0 ] ← opt[i − 1, W 0 ]
6: if wi ≤ W 0 and opt[i − 1, W 0 − wi ] + wi ≥ opt[i, W 0 ] then
7: opt[i, W 0 ] ← opt[i − 1, W 0 − wi ] + wi
8: return opt[n, W ]
22/79
Recover the Optimum Set
1: for W 0 ← 0 to W do
2: opt[0, W 0 ] ← 0
3: for i ← 1 to n do
4: for W 0 ← 0 to W do
5: opt[i, W 0 ] ← opt[i − 1, W 0 ]
6: b[i, W 0 ] ← N
7: if wi ≤ W 0 and opt[i − 1, W 0 − wi ] + wi ≥ opt[i, W 0 ]
then
8: opt[i, W 0 ] ← opt[i − 1, W 0 − wi ] + wi
9: b[i, W 0 ] ← Y
10: return opt[n, W ]
23/79
Recover the Optimum Set
1: i ← n, W 0 ← W, S ← ∅
2: while i > 0 do
3: if b[i, W 0 ] = Y then
4: W 0 ← W 0 − wi
5: S ← S ∪ {i}
6: i←i−1
7: return S
24/79
Running Time of Algorithm
1: for W 0 ← 0 to W do
2: opt[0, W 0 ] ← 0
3: for i ← 1 to n do
4: for W 0 ← 0 to W do
5: opt[i, W 0 ] ← opt[i − 1, W 0 ]
6: if wi ≤ W 0 and opt[i − 1, W 0 − wi ] + wi ≥ opt[i, W 0 ] then
7: opt[i, W 0 ] ← opt[i − 1, W 0 − wi ] + wi
8: return opt[n, W ]
25/79
Avoiding Unncessary Computation and Memory
Using Memoized Algorithm and Hash Map
compute-opt(i, W 0 )
1: if opt[i, W 0 ] 6= ⊥ then return opt[i, W 0 ]
2: if i = 0 then r ← 0
3: else
4: r ← compute-opt(i − 1, W 0 )
5: if wi ≤ W 0 then
6: r0 ← compute-opt(i − 1, W 0 − wi ) + wi
7: if r0 > r then r ← r0
8: opt[i, W 0 ] ← r
9: return r
27/79
Knapsack Problem
Input: an integer bound W > 0
a set of n items, each with an integer weight wi > 0
a value vi > 0 for each item i
Output: a subset S of items that
X X
maximizes vi s.t. wi ≤ W.
i∈S i∈S
28/79
DP for Knapsack Problem
29/79
Exercise: Items with 3 Parameters
30/79
Outline
31/79
Subsequence
A = bacdca
C = adca
C is a subsequence of A
32/79
Longest Common Subsequence
Input: A[1 .. n] and B[1 .. m]
Output: the longest common subsequence of A and B
Example:
A = ‘bacdca0
B = ‘adbcda0
LCS(A, B) = ‘adca0
33/79
Matching View of LCS
b a c d c a
a d b c d a
34/79
Reduce to Subproblems
A = ‘bacdca0
B = ‘adbcda0
either the last letter of A is not matched:
need to compute LCS(‘bacd0 , ‘adbcd0 )
or the last letter of B is not matched:
need to compute LCS(‘bacdc0 , ‘adbc0 )
35/79
Dynamic Programming for LCS
36/79
Dynamic Programming for LCS
1: for j ← 0 to m do
2: opt[0, j] ← 0
3: for i ← 1 to n do
4: opt[i, 0] ← 0
5: for j ← 1 to m do
6: if A[i] = B[j] then
7: opt[i, j] ← opt[i − 1, j − 1] + 1, π[i, j] ← “-”
8: else if opt[i, j − 1] ≥ opt[i − 1, j] then
9: opt[i, j] ← opt[i, j − 1], π[i, j] ←“←”
10: else
11: opt[i, j] ← opt[i − 1, j], π[i, j] ← “↑”
37/79
Example
1 2 3 4 5 6
A b a c d c a
B a d b c d a
0 1 2 3 4 5 6
0 0⊥ 0⊥ 0⊥ 0⊥ 0⊥ 0⊥ 0⊥
1 0⊥ 0← 0← 1- 1← 1← 1←
2 0⊥ 1- 1← 1← 1← 1← 2-
3 0⊥ 1↑ 1← 1← 2- 2← 2←
4 0⊥ 1↑ 2- 2← 2← 3- 3←
5 0⊥ 1↑ 2↑ 2← 3- 3← 3←
6 0⊥ 1- 2↑ 2← 3↑ 3← 4-
38/79
Example: Find Common Subsequence
1 2 3 4 5 6
A b a c d c a
B a d b c d a
0 1 2 3 4 5 6
0 0⊥ 0⊥ 0⊥ 0⊥ 0⊥ 0⊥ 0⊥
1 0⊥ 0← 0← 1- 1← 1← 1←
2 0⊥ 1- 1← 1← 1← 1← 2-
3 0⊥ 1↑ 1← 1← 2- 2← 2←
4 0⊥ 1↑ 2- 2← 2← 3- 3←
5 0⊥ 1↑ 2↑ 2← 3- 3← 3←
6 0⊥ 1- 2↑ 2← 3↑ 3← 4-
39/79
Find Common Subsequence
1: i ← n, j ← m, S ← ()
2: while i > 0 and j > 0 do
3: if π[i, j] =“-” then
4: add A[i] to beginning of S, i ← i − 1, j ← j − 1
5: else if π[i, j] =“↑” then
6: i←i−1
7: else
8: j ←j−1
9: return S
40/79
Variants of Problem
Example:
A = ocurrance, B = occurrence
3 operations: insert ’c’, remove ’a’ and insert ’e’
41/79
Variants of Problem
Example:
A = ocurrance, B = occurrence.
2 operations: insert ’c’, change ’a’ to ’e’
42/79
Edit Distance (with Replacing)
43/79
Exercise: Longest Palindrome
Example:
Input: acbcedeacab
Output: acedeca
44/79
Outline
45/79
Computing the Length of LCS
1: for j ← 0 to m do
2: opt[0, j] ← 0
3: for i ← 1 to n do
4: opt[i, 0] ← 0
5: for j ← 1 to m do
6: if A[i] = B[j] then
7: opt[i, j] ← opt[i − 1, j − 1] + 1
8: else if opt[i, j − 1] ≥ opt[i − 1, j] then
9: opt[i, j] ← opt[i, j − 1]
10: else
11: opt[i, j] ← opt[i − 1, j]
A: We only keep two rows: the (i − 1)-th row and the i-th row.
47/79
Linear Space Algorithm to Compute Length of LCS
1: for j ← 0 to m do
2: opt[0, j] ← 0
3: for i ← 1 to n do
4: opt[i mod 2, 0] ← 0
5: for j ← 1 to m do
6: if A[i] = B[j] then
7: opt[i mod 2, j] ← opt[i − 1 mod 2, j − 1] + 1
8: else if opt[i mod 2, j − 1] ≥ opt[i − 1 mod 2, j] then
9: opt[i mod 2, j] ← opt[i mod 2, j − 1]
10: else
11: opt[i mod 2, j] ← opt[i − 1 mod 2, j]
12: return opt[n mod 2, m]
48/79
How to Recover LCS Using Linear Space?
Only keep the last two rows: only know how to match A[n]
Can recover the LCS using n rounds: time = O(n2 m)
Using Divide and Conquer + Dynamic Programming:
Space: O(m + n)
Time: O(nm)
49/79
Outline
50/79
Directed Acyclic Graphs
c 2 5
b d 1 3 6 8
s a
4 7
not a DAG
a DAG
Lemma A directed graph is a DAG if and only its vertices can be
topologically sorted.
51/79
Shortest Paths in DAG
Input: directed acyclic graph G = (V, E) and w : E → R.
Assume V = {1, 2, 3 · · · , n} is topologically sorted: if
(i, j) ∈ E, then i < j
Output: the shortest path from 1 to i, for every i ∈ V
2 9 5
6 3
1
8
1 2 3 5 6 5 8
9
8 2
1
4 1 7
52/79
Shortest Paths in DAG
53/79
Shortest Paths in DAG
54/79
Example
1 10
2 9 5
6 3
0 1 2 8 7 11
1 2 3 5 6 5 8
9
8 8 1 9 2
4 1 7
55/79
Variant: Heaviest Path in a Directed Acyclic Graph
56/79
Outline
57/79
Matrix Chain Multiplication
58/79
Example:
A1 : 10 × 100, A2 : 100 × 5, A3 : 5 × 50
10 × 100 100 × 5 5 × 50 10 × 100 100 × 5 5 × 50
10 · 100 · 5 100 · 5 · 50
10 × 5 = 5000 = 25000 100 × 50
10 · 5 · 50 10 · 100 · 50
10 × 50 = 2500 = 50000 10 × 50
59/79
Matrix Chain Multiplication: Design DP
60/79
Matrix Chain Multiplication: Design DP
61/79
Constructing Optimal Solution
Print-Optimal-Order(i, j)
1: if i = j then
2: print(“A”i )
3: else
4: print(“(”)
5: Print-Optimal-Order(i, π[i, j])
6: Print-Optimal-Order(π[i, j] + 1, j)
7: print(“)”)
62/79
matrix A1 A2 A3 A4 A5
size 3×5 5×2 2×6 6×9 9×4
63/79
matrix A1 A2 A3 A4 A5
size 3×5 5×2 2×6 6×9 9×4
64/79
matrix A1 A2 A3 A4 A5
size 3×5 5×2 2×6 6×9 9×4
66/79
opt, π j=1 j=2 j=3 j=4 j=5
i=1 0, / 30, 1 66, 2 192, 2 234, 2
i=2 0, / 60, 2 198, 2 220, 2
i=3 0, / 108, 3 180, 4
i=4 0, / 216, 4
i=5 0, /
Print-Optimal-Order(1,5)
Print-Optimal-Order(1, 2)
Print-Optimal-Order(1, 1)
Print-Optimal-Order(2, 2)
Print-Optimal-Order(3, 5)
Print-Optimal-Order(3, 4)
Print-Optimal-Order(3, 3)
Print-Optimal-Order(4, 4)
Print-Optimal-Order(5, 5)
Optimum way for multiplication: ((A1 A2 )((A3 A4 )A5 ))
67/79
Outline
68/79
Optimum Binary Search Tree
69/79
Optimum Binary Search Tree
Example: f1 = 10, f2 = 5, f3 = 3
e1 e2 e3
e2 e1 e3 e2
e3 e1
10 × 1 + 5 × 2 + 3 × 3 = 29
10 × 2 + 5 × 1 + 3 × 2 = 31
10 × 3 + 5 × 2 + 3 × 1 = 43
70/79
suppose we decided to let ek be the root
e1 , e2 , · · · , ek−1 are on left sub-tree
ek+1 , ek+2 , · · · , en are on right sub-tree
dj : depth of ej in our tree
C, CL , CR : cost of tree, left sub-tree and right sub-tree
d1 = 3, d2 = 2, d3 = 3, d4 = 4, d5 = 1,
e5
d6 = 2, d7 = 4, d8 = 3, d9 = 4,
C = 3f1 + 2f2 + 3f3 + 4f4 + f5 +
e2 e6
2f6 + 4f7 + 3f8 + 4f9
e1 e3 e8 CL = 2f1 + f2 + 2f3 + 3f4
e4 e7 e9
CR = f6 + 3f7 + 2f8 + 3f9
C = CL + CR + 9j=1 fj Z
P
71/79
C: cost of left tree
ek
e1 · · · ek−1 ek+1 · · · en
n
X n
X n
X
C= f ` d` = f` (d` − 1) + f`
`=1 `=1 `=1
k−1
X n
X n
X
= f` (d` − 1) + f` (d` − 1) + f`
`=1 `=k+1 `=1
n
X
= CL + CR + f`
`=1
72/79
n
X
C = CL + CR + f`
`=1
In general, opt[i, j] =
(
0 if i = j + 1
mink:i≤k≤j opt[i, k − 1] + opt[k + 1, j] + j`=i f`
P
if i ≤ j
73/79
Optimum Binary Search Tree
1: f sum[0] ← 0
2: for i ← 1 to n do f sum[i] ← f sum[i − 1] + fi
. f sum[i] = ij=1 fj
P
3: for i ← 0 to n do opt[i + 1, i] ← 0
4: for ` ← 1 to n do
5: for i ← 1 to n − ` + 1 do
6: j ← i + ` − 1, opt[i, j] ← ∞
7: for k ← i to j do
8: if opt[i, k − 1] + opt[k + 1, j] < opt[i, j] then
9: opt[i, j] ← opt[i, k − 1] + opt[k + 1, j]
10: π[i, j] ← k
11: opt[i, j] ← opt[i, j] + f sum[j] − f sum[i − 1]
74/79
Printing the Tree
Print-Tree(i, j)
1: if i > j then
2: return
3: else
4: print(’(’)
5: Print-Tree(i, π[i, j] − 1)
6: print(π[i, j])
7: Print-Tree(π[i, j] + 1, j)
8: print(’)’)
75/79
Outline
76/79
Dynamic Programming
Break up a problem into many overlapping sub-problems
Build solutions for larger and larger sub-problems
Use a table to store solutions for sub-problems for reuse
77/79
Comparison with greedy algorithms
Greedy algorithm: each step is making a small progress towards
constructing the solution
Dynamic programming: the whole solution is constructed in the
last step
78/79
Definition of Cells for Problems We Learnt
79/79