Dynamic Programming 2025 Part 2
Dynamic Programming 2025 Part 2
20 juin 2025
Outline
Matrix-chain multiplication
Floyd-Warshall algorithm
Conclusion
Final exercises
Matrix-Chain Multiplication Problem (MCM)
Computing the product as (A1 (A2 (A3 A4 ))) requires 208,500 scalar
multiplications, and computing it as ((A1 A2 )(A3 A4 )) requires 456,000
scalar multiplications.
(A1 ( A2 ( A3 A4 )) ) ( (A1 A2 ) ( A3 A4 ))
100x3 3x20 20x50 50x150 100x3 3x20 20x50 50x150
A1 A2 A3 A4 A1 A2 A3 A4
A1 A2 B A1 A2 B
The optimal solution M(1, n) will be the product of two fully parenthesized matrices
that looks like (A1 · · · Ak )(Ak+1 · · · An ), where 1 ≤ k < n, where the two half
sequences are fully parenthesized as well.
Given that each value of k, 1 ≤ k < n, divides the n matrices into two different full
parenthesized matrices, the optimal solution is the value of k for which the number
of scalar multiplications is minimized :
int M(i, j)
if i == j then return (0) ; /* based case, a single matrix has 0 scalar multiplication */
else
return mini≤k<j (M(i, k) + M(k + 1, j) + di−1 dk dj ) ;
M[2,2] M[3,4] M[2,3] M[4,4] M[1,1] M[2,2] M[3,3] M[4,4] M[1,1] M[2,3] M[1,2] M[3,3]
M[1,4]
=Already Computed
M[2,2] M[3,4] M[2,3] M[4,4] M[1,1] M[2,2] M[3,3] M[4,4] M[1,1] M[2,3] M[1,2] M[3,3]
▶ Notice that many of the calls are repeated (all the shaded boxes).
▶ The divide-&-conquer algorithm has the following recurrence
n−1
X
T (n) = (T (k) + T (n − k)) + cn
k=1
If n > 0,
n−1
X
T (n) = 2 T (k) + cn
k=1
Therefore Pn−1
T (n) − T (n − 1) = (2 k=1 T (k) + cn)
Pn−2
− (2 k=1 T (k) + c(n − 1))
= 2T (n − 1) + c
That is
T (n) = 3T (n − 1) + c
Analysis of the recurrence for MCM
T (n) = 3T (n − 1) + c
= 3(3T (n − 2) + c) + c
= 9T (n − 2) + 3c + c
= 9(3T (n − 3) + c) + 3c + c
= 27T (n − 3) + 9c + 3c + c
= 3k T (n − k) + c k−1 3l
P
n Pn−1 l=0
l
= 3 T (0) + c l=0 3
n
= c3n + c 3 2−1
= (c + c2 )3n − c2
T (n) ∈ Θ(3n ).
This recurrence can be solved using the recursion tree method. There are n − 1
levels, each level i execute 3i c operations, yielding the following summation :
30 + 31 + 32 + · · · + 3n−1 , the dominant term in this expression is 3n−1 ∈ Θ(3n )
Optimal Substructure
int M(i, j)
if i == j then return (0) ;
else
return mini≤k<j (M(i, k) + M(k + 1, j) + di−1 dk dj ) ;
1 2 3 4
1
2
3
4
This
Pn algorithm has 3 nested loops. The summation is
Pn−1 Pi+s
s=1 i=1 k=i 1. The complexity of dynamic programming MCM is
3
Θ(n ).
Bottom up DP solution for MCM
Base cases are subsequences of length 1 such as [1, 1], [2, 2], . . ., [n, n]. The base cases will
appear in the diagonal M[i, i] of the n × n table, the number of scalar multiplications is 0.
1 2 3 4
1 0
2 0
3 0
4 0
A dynamic programming solution to MCM
int M(i, j)
if i == j then return (0) ;
else
return mini≤k<j (M(i, k) + M(k + 1, j) + di−1 dk dj ) ;
The next larger problem sizes to solve involve the product of two consecutive
matrices, the subproblems are subsequences of length 2.
We multiple each 2 consecutive matrices, i.e. (1, 2), (2, 3), (n − 1, n), and store the
solution into M[i, i + 1]
Here k can only take one value, k = i. According to the recursive algo
M[i, i + 1] = M[i, i] + M[i + 1, i + 1] + di−1 dk dj = di−1 dk dj
1 2 3 4 5
0 d0 d1 d2 1
0 d1 d2 d3 2
0 d2 d3 d4 3
0 d3 d4 d5 4
0 5
A dynamic programming solution to MCM
int M(i, j)
if i == j then return (0) ;
else
return mini≤k<j (M(i, k) + M(k + 1, j) + di−1 dk dj ) ;
The next larger problem sizes to solve involve the product of three
consecutive matrices.
We multiple each 3 consecutive matrices, i.e. (1, 3), (2, 4), (n − 2, n),
and store the solution into M[i, i + 2]
Here k can only take two values, k = i and k = i + 1. According to the
k<j
recursive algo M[i, i + 2] = mink=i (M[i, k] + M[k + 1, j] + di−1 dk dj )
1 2 3 4 5
0 d0 d1 d2 ? 1
0 d1 d2 d3 ? 2
0 d2 d3 d4 ? 3
0 d3 d4 d5 4
0 5
MCM dynamic programming Example
1 2 3 4 5 1 2 3 4 5
0 120 1 0 120 264 1
0 360 2 0 360 1320 2
⇒
0 720 3 0 720 1140 3
0 1680 4 0 1680 4
0 5 0 5
MCM Example (continued again)
1 2 3 4 5 1 2 3 4 5
0 120 264 1 0 120 264 1080 1
0 360 1320 2 0 360 1320 1350 2
⇒
0 720 1140 3 0 720 1140 3
0 1680 4 0 1680 4
0 5 0 5
MCM Example (continued again)
1 2 3 4 5 1 2 3 4 5
0 120 264 1080 1 0 120 264 1080 1344 1
0 360 1320 1350 2 0 360 1320 1350 2
⇒
0 720 1140 3 0 720 1140 3
0 1680 4 0 1680 4
0 5 0 5
MCM Example : Optimal Cost and Solution
Each time the optimal value for M[i, j] is found, store also the value of
k that was used.
1 2 3 4 5
0 120/1 264/2 1080/2 1344/2 1
0 360/2 1320/2 1350/2 2
0 720/3 1140/4 3
0 1680/4 4
0 5
The optimal solution for the second half comes from entry M[3, 5].
There are two ways one can split a sequence of 3 matrices in two
sub-sequences : (1) (2,3) or (1,2) (3). Given 5 matrices there are 3
sequences of 3 matrices. (2, 3, 5, 2, 4, 3)
M[1, 2] + M[3, 3] + d0 d2 d3 = 30 + 0 + 2 · 2 · 2 = 50
M[1, 3] = min
M[1, 1] + M[2, 3] + d0 d1 d3 = 0 + 30 + 2 · 3 · 2 = 42
M[2, 3] + M[4, 4] + d1 d3 d4 = 30 + 0 + 3 · 2 · 4 = 54
M[2, 4] = min
M[2, 2] + M[3, 4] + d1 d2 d4 = 0 + 40 + 3 · 5 · 4 = 100
M[3, 4] + M[5, 5] + d2 d4 d5 = 40 + 0 + 5 · 4 · 3 = 100
M[3, 5] = min
M[3, 3] + M[4, 5] + d2 d3 d5 = 0 + 24 + 5 · 2 · 3 = 54
1 2 3 4 5
0 30/1 42/1 1
0 30/2 54/3 2
0 40/3 54/3 3
0 24/4 4
0 5
Solving exercise 6
1 2 3 4 5
0 30/1 42/1 58/3 1
0 30/2 54/3 72/3 2
0 40/3 54/3 3
0 24/4 4
0 5
Solving exercise 6
1 2 3 4 5
0 30/1 42/1 58/3 78/3 1
0 30/2 54/3 72/3 2
0 40/3 54/3 3
0 24/4 4
0 5
Solving exercise 6
Let lcs(X [1..m], Y [1..n]) be the length of the LCS of X and Y . The
length of the LCS is computed recursively as follow :
Building a partial call tree for sequences AXYT and AYZX , it can be
verified that the recursive algorithm solves the same subproblems
several times. Soon you will observe that lcs(”AXY”, ”AYZ”) is being
solved twice.
Top Down DP algorithm for LCS
X = [ABCBDAB]
Y = [BDCABA]
m = 7; n = 6
LCS is [BCBA]
Printing the LCS
Print-LCS(b,X,i,j)
if i == 0 or j == 0 return
if b[i, j] == ”↖”
Print-LCS(b,X,i-1,j-1)
print xi
else if b[i, j] == ”↑”
Print-LCS(b,X,i-1,j)
else if b[i, j] == ”←”
Print-LCS(b,X,i,j-1)
In this graph there are several paths between vertices 0,6 beside the one we found
above : (0,2,7,5,1,3,6), (0,4,7,3,6), (0,4,7,5,1,3,6), (0,4,5,7,3,6), etc.
The All-Pairs Shortest-Path Problem
The dynamic programming algorithm we study to solve this problem is called the
Floyd-Warshall algorithm.
First find a way to reduce the problem size
Let assume the n nodes in the graph are numbered consecutively from 1 to n
We seek the shortest path between a pair of nodes i, j. Initially this path has the
option to pass through any intermediary nodes in the set {1, 2, . . . , n} \ {i, j}.
Once a node k has been excluded from the intermediary set, one must decide what
to do with k.
There are two choices :
1. k is made mandatory part of the path between i and j,
2. k is excluded permanently from this path.
Building sub-problems : base case
If recursively we keep removing vertices from the intermediary set, this set is
eventually empty.
Furthermore, if the removed nodes are never considered to be include in the path,
the cost of the shortest path between a pair of nodes i and j is the cost of the direct
edge, this is the base case.
Assume the intermediate set in the graph below is empty, there is no edge between
nodes 0 and 2, thus the length of the shortest path, the self-solution, base case, is
∞
Building sub-problems : keeping k in the path
Assume the removed node k is made a mandatory part of the shortest pth between
nodes i and j
Consider the graph below, where node 4 is removed from the intermediate set,
leaving this set to be {1, 3}.
Furthermore, node 4 is made a necessary part of the shortest path between 0 and 2.
Having node 4 in the path between 0 and 2 creates two new shortest path
sub-problems :
▶ one between 0 and 4 and
▶ another between 4 and 2
Where both sub-problems have intermediate set {0, 1, 2, 3, 4} \ {0, 2, 4} = {1, 3}
A divide-&-conquer algo for all-pairs shortest path
Denote the function that returns the cost of the shortest path between
nodes i, j using only intermediary nodes from the set {1, 2, . . . , k} as
Path(i, j, k)
Base case : the value returns by Path(i, j, ∅) is the cost of the direct
edge from i to j in the graph, i.e. Path(i, j, ∅) = L[i, j]
Costs of shortest paths in a divide-&-conquer algo
Assume the nodes in the graph are numbered consecutively from 1 to n. There are
two possible ways one can compute the cost of Path(i, j, k)
1. node k is not part of the shortest path between i, j, the cost of
Path(i, j, k) = Path(i, j, k − 1) where Path(i, j, k − 1) is a new subproblem
solved using nodes from the set {1, 2, . . . , k − 1}
2. node k is part of the shortest path between i, j. Then there are two
subproblems : path from i to k and path from k to j using only intermediate
nodes {1, 2, . . . , k − 1}.
function Path(i, j, k)
if (k == ∅) then return L[i, j] ; /* Base case */
else
return min(Path(i, j, k − 1),
Path(i, k, k − 1) + Path(k, j, k − 1))
The above algorithm is called for each pair of node i, j in a graph. It is also used to
design the Floyd-Warshall dynamic programming algorithm that solves the all pairs
of shortest paths problem.
Call tree for D-&-C between nodes 0 and 2
{1,3,4} 4
P(0,2,4)
4
{1,3} P(0,2,3)
8 ∞ ∞ 5
P(0,3,1)+P(3,4,1) P(4,2,1) P(4,3,1)+P(3,2,1) {1}
P(0,4,1)
The function Path(i, j, k) has to be called for each pair of nodes i, j in the graph.
Already from the previous small example, we see subproblems been computed
repetitively, for example P(0, 3, 1) or P(3, 2, ∅)
The problem satisfies the optimal substructure property, the optimal solution to
Path(i, j, k) is based on the optimal solution of Path(i, k, k − 1) and
Path(k, j, k − 1). Suppose this is not the case, and assume that the subpath i to k
is not optimal. Then there exists another subpath i to k with cost
Path(i, k, k − 1)′ < Path(i, k, k − 1) which contradicts that Path(i, j, k) is optimal.
Floyd-Warshall Algorithm
It is a bottom up dynamic programming algorithm. Considering the nodes in the
graph are numbered consecutively from 1 to n, the DP algorithms starts by
computing the shortest path for all pairs of nodes for k = 0 (no intermediate node).
Then it considers k = 1, k = 2, until k = n.
After each iteration k, Dk contains the length of the shortest paths that only use
nodes in {1, 2, . . . , k} as intermediate nodes.
Floyd Algorithm
At iteration k, the algo checks each pair of nodes (i, j) whether or not there exists a
path from i to j passing through node k that is better than the present optimal
path passing only through nodes in {1, 2, . . . , k − 1}.
For k = 1, compute the shortest path between each pair of nodes (i, j)
when the path is allowed to pass through node 1.
Execution of Floyd’s algorithm
Algorithm Floyd(L[n, n])
D0 = L
for (k = 1; k ≤ n; k + +)
for (i = 1; i ≤ n; i + +)
for (j = 1; j ≤ n; j + +)
D[2, i, j] = min(D[1, i, j], D[1, i, k] + D[1, k, j])
return Dk
For k = 2, compute the shortest path between each pair of nodes (i, j)
when the path is allowed to pass through nodes 1 and 2.
Execution of Floyd’s algorithm
Algorithm Floyd(L[n, n])
D0 = L
for (k = 1; k ≤ n; k + +)
for (i = 1; i ≤ n; i + +)
for (j = 1; j ≤ n; j + +)
D[3, i, j] = min(D[2, i, j], D[2, i, k] + D[2, k, j])
return Dk
For k = 3, compute the shortest path between each pair of nodes (i, j)
when the path is allowed to pass through nodes {1, 2, 3}.
Execution of Floyd’s algorithm
Algorithm Floyd(L[n, n])
D0 = L
for (k = 1; k ≤ n; k + +)
for (i = 1; i ≤ n; i + +)
for (j = 1; j ≤ n; j + +)
D[4, i, j] = min(D[3, i, j], D[3, i, k] + D[3, k, j])
return Dk
0 0 0 0
0 0 0 0
0 1 0 0
0 1 0 0
Computing the shortest paths : k = 2
0 0 2 2
0 0 0 0
0 1 0 0
0 1 0 0
Computing the shortest paths : k = 3
0 0 2 2
3 0 0 0
0 1 0 0
0 1 0 0
Computing the shortest paths : k = 4
0 0 4 2
4 0 4 0
0 1 0 0
0 1 0 0
Computing the shortest paths
0 0 4 2
4 0 4 0
P=
0
1 0 0
0 1 0 0
10. Compute the all pairs of shortest paths for the following oriented
graph.
0 5 10 3
∞ 0 1 4
L=
∞
∞ 0 ∞
∞ ∞ 2 0
11. Compute the all pairs of shortest paths for the following oriented
graph.
0 3 8 ∞ 4
∞ 0 ∞ 1 7
∞
L= 4 0 ∞ ∞
2 ∞ 5 0 ∞
∞ ∞ ∞ 6 0
0
0 1 ∞ ∞ ∞
1
∞ 0 1 3 ∞
1 8
D0 = L = ∞ ∞ 0 1 6
2 1
6 1
8 ∞ ∞ 0 3 3
4 3
∞ ∞ ∞ ∞ 0 3
−1 −1 1 2 3
3
−1 −1 2 3
3
P= 3 0−1 −1 3
−1 0 1 −1 −1
−1 −1 −1 −1 −1
-1 means the shortest path between nodes x and y is the directed edge
in the graph. Shortest path between :
▶ (0, 2) = (0, 1)(1, 2) = (0, 1, 2)
▶ (0, 3) = (0, 2)(2, 3) where (0, 2) = (0, 1)(1, 2) = (0, 1, 2, 3)
▶ (0, 4) = (0, 3)(3, 4) = (0, 1, 2, 3, 4)
▶ (1, 0) = (1, 3)(3, 0) where (1, 3) = (1, 2)(2, 3) = (1, 2, 3) = (1, 2, 3, 0)
▶ (1, 3) = (1, 2)(2, 3) = (1, 2, 3)
▶ (1, 4) = (1, 3)(3, 4) = (1, 2, 3, 4)
▶ (2, 0) = (2, 3)(3, 0) = (2, 3, 0)
▶ (2, 1) = (2, 3)(3, 1) = (2, 3, 1)
▶ (2, 4) = (2, 3)(3, 4) = (2, 3, 4)
▶ (3, 1) = (3, 0)(0, 1) = (3, 0, 1)
▶ (3, 2) = (3, 1)(1, 2) where (3, 1) = (3, 0)(0, 1) = (3, 0, 1) = (3, 0, 1, 2)
Computing the binomial coefficient
function C (n, k)
if (k == 0 or k == n) then return 1
else return C (n − 1, k − 1) + C (n − 1, k)
4
2
3 3
1 2
2 2 2 2
0 1 1 2
1 1 1 1
0 1 0 1
Complexity of recursive binomial
function C dyn(n, k)
int *f, i, j ;
f = malloc((n × k) ∗ sizeof(int)) ;
for (i = 0; i ≤ n; i + +)
for (j = 0; j ≤ min(i, k); j + +)
if ((j == 0)||(j == i) then
f [i, j] = 1 ;
else
if (j ≤ k) then f [i, j] = f [i − 1, j − 1] + f [i − 1, j] ;
return f [n, k] ;
DP algorithm for binomial coefficient
0 1 2
0
function C dyn(n, k) 1
int *f, i, j ; 2
f = malloc((n × k) ∗ sizeof(int)) ; 3
for (i = 0; i ≤ n; i + +) 4
for (j = 0; j ≤ min(i, k); j + +)
if ((j == 0)||(j == i) then
f [i, j] = 1 ;
else
if (j ≤ k) then f [i, j] =
f [i − 1, j − 1] + f [i − 1, j] ;
return f [n, k] ;
Cost of dynamic programs
Calculate how many table or list entries you fill in (sometimes you
don’t use the entire table, just all entries under the diagonal or
something like that).
Then add the cost of retrieving the answer from the table.
Example : problem fails the optimal substructure test
2. Base on your recursive algorithm, generate a table to store the results of a dynamic
programming algorithm and fill the table for the strings ABAZDC and BACBAD
1 2 3 4 5 6
A B A Z D C
0 0 0 0 0 0 0
1 B 0 0 1 0 0 0 0
2 A 0 1 0 2 0 0 0
3 C 0 0 0 0 0 0 1
4 B 0 0 1 0 0 0 0
5 A 0 1 0 2 0 0 0
6 D 0 0 0 0 0 1 0