Dynamic Programming

Shortest Acyclic Path

Let G=(V,E)G = (V,E) be a directed acyclic graph, where each edge (u,v)(u, v) has a weight wt(u,v)>0wt(u, v) > 0.

Topological sorting: vertices are numbered v1,v2,...,vnv_1, v_2, ..., v_n such that for each (vi,vj)(v_i, v_j), we have i<ji< j.

Let s=v1s = v_1 and t=vnt = v_n.

How do we compute the shortest path from ss to tt?

Can we do better than Dijkstra algorithm, using the topological ordering of GG?

Step 1: Structure of the Optimal Solution

Let u1,u2,...,uku_1, u_2, ..., u_k be all vertices that have an edge to tt.

The last edge of the shortest path from ss to tt is (ui,t)(u_i, t) for some 1ik1 \leq i \leq k.

If we know this index ii, then the shortest path from ss to tt is equal to path from ss to uiu_i followed by the edge (ui,t)(u_i, t).

Since we don't know the index ii, the length of the shortest path from ss to tt is equal: min1ik\mathrm{min}_{1\leq i \leq k} (length of the shortest path from ss to uiu_i) + wt(ui,t)wt(u_i, t). In other words, the shortest path from ss to tt contains the shortest path from ss to one of u1,u2,...,uku_1, u_2, ..., u_k.

Step 2: Set up a Recurrence for the Optimal Solution

For j=1,2,...,nj = 1,2,..., n, define d(vj)=d(v_j) = length of a shortest path from ss to vjv_j. Since vn=tv_n = t, we want to compute d(vn)d(v_n).

Recurrence:

  • d(v1)=0d(v_1) = 0
  • For 2jn2 \leq j \leq n, d(vj)=min(vi,vj)Ed(vi)+wt(vi,vj)d(v_j) = \mathrm{min}_{(v_i, v_j) \in E} {d(v_i) + wt(v_i, v_j)}

Step 3: Solve the Recurrence "Bottom-Up"

First idea:

  • To compute d(vn)=d(t)d(v_n) = d(t), take all edges (u1,t),(u2,t),...,(uk,t)(u_1, t), (u_2, t), ..., (u_k, t), and recursively compute d(u1),d(u2),...,d(uk)d(u_1), d(u_2), ..., d(u_k). From this, compute d(vn)d(v_n) as d(vn)=min1ikd(ui)+wt(ui,t)d(v_n) = \mathrm{min}_{1\leq i \leq k} {d(u_i) + wt(u_i, t)}

This is slow and takes O(2n)O(2^n).

Better approach:

d(v1) = 0
for i in range(2, n):
k = indegree(vj)
Let u1, u2, ..., uk be all the vertices that have an edge to vj
d(vj) = infinity
for i in range(1, k):
d(vj) = min {d(vj), d(ui) + wt(ui, vj)}
return d(vn)

This running time here is O(V+E)O(|V| + |E|).

Matrix Chain Multiplication

A:pxqA: p x q matrix, B:qxrB: q x r matrix, C=AB:pxrC = A \cdot B: p x r matrix. CC has prpr entries, each of which can be computed in O(q)O(q) time. So CC can be computed in O(pqr)O(pqr) time. We define the cost of multiplying AA and BB to be pqrpqr.

In general,

  • p0,p1,...,pnp_0, p_1, ..., p_n: positive integers
  • A1,A2,...,AnA_1, A_2, ..., A_n: matrices such that AiA_i has pi1p_{i-1} rows and pip_i columns.

Compute the best order to compute the product of the matrices by minimizing the total cost.

Step 1: Structure of the Optimal Solution

Consider the best order to compute AiAi+1...AjA_iA_{i+1} \cdot ... \cdot A_j. In the last step, we multiply: (Ai...Ak)(A_i \cdot ... \cdot A_k) and (A_{k+1} \cdot ... \cdot A_j), both of which are already computed.

Step 2: Set Up a Recurrence for the Optimal Solution

For 1ijn1\leq i \leq j \leq n, define m(i,j)m(i,j) = minimum cost to compute Ai...AjA_i \cdot ... \cdot A_j.

We want to compute m(1,n)m(1,n).

If we know kk, then m(i,j)=m(i,k)+m(k+1,j)+pi1pkpjm(i,j) = m(i,k) + m(k+1, j) + p_{i-1}p_kp_j.

But we do not know kk, so try all values of kk, ikj1i \leq k \leq j-1 and take the best one.

Recurrence:

  • For 1in1\leq i \leq n: m(i,i)=0m(i, i) = 0.
  • For 1i<jn1 \leq i < j \leq n: m(i,j)=minikj1m(i,k)+m(k+1,j)+pi1pkpjm(i,j) = \mathrm{min}_{i \leq k \leq j-1} m(i,k) + m(k+1, j) + p_{i-1}p_kp_j

Step 3: Solve the Recurrence Bottom-Up

Compute, in this order,
m(1,1),m(2,2),...,m(n,n)m(1, 1), m(2, 2), ..., m(n, n)
m(1,2),m(2,3),...,m(n1,n)m(1, 2), m(2, 3), ..., m(n - 1, n)
m(1,3),m(2,4),...,m(n2,n)m(1, 3), m(2, 4), ..., m(n - 2, n)
m(1,4),m(2,5),...,m(n3,n)m(1, 4), m(2, 5), ..., m(n - 3, n)
.
.
.
m(1,n1),m(2,n)m(1, n - 1), m(2, n)
m(1,n)m(1, n)

Algorithms

for i = 1 to n do
m(i, i) = 0
for l = 2 to n do
// Compute m(1, l), m(2, l + 1), ..., m(n - l + 1, n)
for i = 1 to n - l + 1 do
// Compute m(i, i + l - 1)
j = i + l - 1
// Compute m(i, j) using the recurrence
m(i, j) =
for k = i to j - 1 do
m(i, j) = min {m(i, j), m(i, k) + m(k + 1, j) + pi-1*pk*pj}
return m(1, n)

Running time is Θ(n3)\Theta(n^3)

Longest Common Subsequence

We have two sequences:

  • X=(a,b,c,b,d,a,b)X = (a,b,c,b,d,a,b)
  • Y=(b,d,c,a,b,a)Y = (b,d,c,a,b,a)

The sequence Z=(b,c,d,b)Z = (b,c,d,b) is a subsequence of XX but not of YY.

LCS(XX, YY) is the longest subsequence of XX and YY: (b,c,b,a)(b,c,b,a) or (b,d,a,b)(b,d,a,b)

Step 1: Structure of the Optimal Solution

X=(x1,x2,...,xm)X = (x_1, x_2, ..., x_m) Y=(y1,y2,...,yn)Y = (y_1, y_2, ..., y_n)

Z=(z1,z2,...,zk)=Z = (z_1, z_2, ..., z_k) = LCS(XX, YY). Therefore km,knk \leq m, k \leq n

  • Case 1:
    • zk=xm=ynz_k = x_m = y_n and (z1,z2,...,zk1)=LCS(x1x2...xm1,y1y2...yn1)(z_1, z_2, ..., z_{k-1}) = \mathrm{LCS}(x_1x_2 ... x_{m-1}, y_1y_2 ... y_{n-1})
  • Case 2:
    • xmynx_m \neq y_n
    • Case 2a): zkxmz_k \neq x_m
      • (z1,z2,...,zk)=LCS(x1x2...xm1,y1y2...yn)(z_1, z_2, ..., z_{k}) = \mathrm{LCS}(x_1x_2 ... x_{m-1}, y_1y_2 ... y_{n})
    • Case 2b): zkynz_k \neq y_n
      • (z1,z2,...,zk)=LCS(x1x2...xm,y1y2...yn1)(z_1, z_2, ..., z_{k}) = \mathrm{LCS}(x_1x_2 ... x_{m}, y_1y_2 ... y_{n-1})
    • We compute both 2a and 2b as we don't know which case we fall under

Step 2: Set up a recurrence for the Optimal Solution

For 0im0 \leq i \leq m and 0jn0 \leq j \leq n define c(i,j)=c(i,j) = length of LCS(x1x2...xi,y1y2...yj)\mathrm{LCS}(x_1x_2...x_i, y_1y_2...y_j)

We want to compute c(m,n)c(m,n)

Recurrence:

  • If i=0i=0 or j=0j=0, c(i,j)=0c(i,j) = 0
  • If i1,j1i\geq 1, j \geq 1 and xi=yjx_i = y_j, c(i,j)=1+c(i1,j1)c(i,j) = 1 + c(i-1, j-1)
  • If i1,j1i\geq 1, j \geq 1 and xiyjx_i \neq y_j, c(i,j)=maxc(i1,j),c(i,j1)c(i,j) = max{c(i-1,j), c(i, j-1)}

Step 3: Solve the recurrence "Bottom-Up"

Fill in the matrices c(i,j)c (i, j) for 0im0 \leq i \leq m and 0jn0 \leq j \leq n.

  • First row:
    • c(0,0)=c(0,1)=...=c(0,n)=0c(0, 0) = c(0, 1) = ... = c(0, n) = 0
  • First column:
    • c(0,0)=c(1,0)=...=c(m,0)=0c(0, 0) = c(1, 0) = ... = c(m, 0) = 0

Then fill in the matrix, row by row, in each row from left to right.

Algorithm

for i = 0 to m do
c(i, 0) = 0
for j = 0 to n do
c(0, j) = 0
for i = 1 to m do
for j = 1 to n do
if xi = yj then
c(i, j) = 1 + c(i - 1, j - 1)
else
c(i, j) = max {c(i - 1, j), c(i, j - 1)}}
return c(m, n)

Running time: O(mn)O(m*n)

Space: O(mn)O(m*n)

But if we only want to compute C(m,n)C(m, n), we only need the current row and the previous row. Hence, Space: O(m+n)O(m+n)

General Structure

In general, when we solve a problem using dynamic programming, we go through the following steps:

  1. Understand the structure of the optimal solution. Show that there is an optimal substructure: an optimal solution “contains” optimal solutions for subproblems (which are smaller problems).
  2. Set up a recurrence for the optimal solution. We know from Step 1 that an optimal solution can be obtained from optimal solutions for subproblems. Use this to derive recurrence relations.
  3. Solve the recurrence bottom-up. First solve the smallest subproblems (usually the base case of the recurrence). Then solve the second smallest subproblems. Then solve the third smallest subproblems, etc.

Do not use a recursive algorithm!

All-Pairs Shortest Paths

Let G=(V,E)G = (V, E) be a directed graph, where V=1,2,...,nV = {1, 2, ..., n}. Each edge (i,j)(i, j) has a weight wt(i,j)>0wt(i, j) > 0.

For all ii and jj, compute the weight of a shortest path in GG from ii to jj, which we denote by γG(i,j)\gamma_G (i, j).

Step 1: Structure of the Optimal Solution

Consider the shortest path from ii to jj, and assume this path has at least one interior vertex. Let kk be the largest interior vertex.

  • shortest path from ii to kk, all interior vertices are k1\leq k-1
  • shortest path from kk to jj, all interior vertices are k1\leq k-1

Step 2: Set up a recurrence for the Optimal Solution

For iini \leq i \leq n, 1jn1 \leq j \leq n, 0kn0 \leq k \leq n, be the length of the shortest path from ii to jj, all of whose interior vertices are vertices are k\leq k.

We want to compute dist(i,j,n)=γG(i,j)dist(i,j,n) = \gamma_G (i,j) for all 1in,1jn1 \leq i \leq n, 1 \leq j \leq n.

Recurrence:

  • For 1in,dist(i,i,0)=01 \leq i \leq n, \mathrm{dist}(i,i, 0) = 0

  • For 1in,1jn,ij1 \leq i \leq n, 1 \leq j \leq n, i \neq j

  • dist(i,j,0)=wt(i,j)if(i,j)isanedge,otherwisedist (i,j,0) = {wt(i,j) if (i,j) is an edge, \infty otherwise}

  • For 1in,1jn,1kn1 \leq i \leq n, 1 \leq j \leq n, 1 \leq k \leq n, dist(i,j,k)=mindist(i,j,k1),dist(i,k,k1)+dist(k,j,k1)dist(i,j,k) = min {dist(i, j, k-1), dist(i, k, k-1) + dist(k, j, k-1)}.

Algorithm: Floyd-Warshall

for i = 1 to n do
for j = 1 to n do
if i = j then
dist(i, j, 0) = 0
else
dist(i, j, 0) = +infinity
for all edges (i, j) do
dist(i, j, 0) = wt(i, j)
for k = 1 to n do
for i = 1 to n do
for j = 1 to n do
dist(i, j, k) = min {dist(i, j, k - 1), dist(i, k, k - 1) + dist(k, j, k - 1)}

O(n3)O(n^3)

Final Remarks

Dynamic Programming solutions work for finding shortest path, minimum cost, but not longest or max. That is a NP-Hard problem