simple path_ problem does not: the longest simple path from u u u to v v v may not contain the longest simple path from u u u to an intermediate vertex w w w Because the subpath might share vertices with the rest of the path, creating a non-simple path.
1D DP. d p [ i ] dp[i] d p [ i ] depends on d p [ j ] dp[j] d p [ j ] for j < i j < i j < i . Example: Fibonacci, longest increasing subsequence.
2D DP. d p [ i ] [ j ] dp[i][j] d p [ i ] [ j ] depends on d p [ i ′ ] [ j ′ ] dp[i'][j'] d p [ i ′ ] [ j ′ ] for ( i ′ , j ′ ) (i', j') ( i ′ , j ′ ) in some set. Example: edit distance, Matrix chain multiplication, longest common subsequence.
Interval DP. d p [ i ] [ j ] dp[i][j] d p [ i ] [ j ] depends on d p [ i ′ ] [ j ′ ] dp[i'][j'] d p [ i ′ ] [ j ′ ] where i ≤ i ′ ≤ j ′ ≤ j i \leq i' \leq j' \leq j i ≤ i ′ ≤ j ′ ≤ j . Example: Optimal BST, matrix chain multiplication.
Problem. Given n n n items with weights w 1 , … , w n w_1, \ldots, w_n w 1 , … , w n and values v 1 , … , v n v_1, \ldots, v_n v 1 , … , v n And a knapsack of capacity W W W Maximise the total value without exceeding the capacity.
Recurrence:
d p [ i ] [ c ] = { 0 i f i = 0 o r c = 0 d p [ i − 1 ] [ c ] i f w i > c max ( d p [ i − 1 ] [ c ] , d p [ i − 1 ] [ c − w i ] + v i ) i f w i ≤ c dp[i][c] = \begin{cases} 0 & \mathrm{if} i = 0 \mathrm{ or} c = 0 \\ dp[i-1][c] & \mathrm{if} w_i > c \\ \max(dp[i-1][c], dp[i-1][c - w_i] + v_i) & \mathrm{if} w_i \leq c \end{cases} d p [ i ] [ c ] = ⎩ ⎨ ⎧ 0 d p [ i − 1 ] [ c ] max ( d p [ i − 1 ] [ c ] , d p [ i − 1 ] [ c − w i ] + v i ) if i = 0 or c = 0 if w i > c if w i ≤ c
Time: O ( n W ) O(nW) O ( nW ) . Space: O ( n W ) O(nW) O ( nW ) (can be reduced to O ( W ) O(W) O ( W ) with 1D array).
Proof of correctness. For each item i i i Either we don’t include it (value d p [ i − 1 ] [ c ] dp[i-1][c] d p [ i − 1 ] [ c ] ) or we include it (value v i + d p [ i − 1 ] [ c − w i ] v_i + dp[i-1][c - w_i] v i + d p [ i − 1 ] [ c − w i ] ). The optimal choice is the maximum. The base cases are correct. ■ \blacksquare ■
Worked Example: 0/1 Knapsack Items: ( w = 1 , v = 1 ) , ( w = 3 , v = 4 ) , ( w = 4 , v = 5 ) , ( w = 5 , v = 7 ) \\{(w=1, v=1), (w=3, v=4), (w=4, v=5), (w=5, v=7)\\} ( w = 1 , v = 1 ) , ( w = 3 , v = 4 ) , ( w = 4 , v = 5 ) , ( w = 5 , v = 7 ) Capacity W = 7 W = 7 W = 7 .
Building the DP table (items as rows, capacities 0-7 as columns):
Maximum value: d p [ 4 ] [ 7 ] = 9 dp[4][7] = 9 d p [ 4 ] [ 7 ] = 9 (items 2 and 4: w = 3 + 5 = 7 w = 3 + 5 = 7 w = 3 + 5 = 7 , v = 4 + 7 = 11 v = 4 + 7 = 11 v = 4 + 7 = 11 — let me recalculate).
Correct: items 2 and 3 (w = 3 + 4 = 7 w=3+4=7 w = 3 + 4 = 7 , v = 4 + 5 = 9 v=4+5=9 v = 4 + 5 = 9 ), or items 1, 2, 4 (w = 1 + 3 + 5 = 9 > 7 w=1+3+5=9 > 7 w = 1 + 3 + 5 = 9 > 7 Not valid). Items 1, 3 (w = 1 + 4 = 5 w=1+4=5 w = 1 + 4 = 5 , v = 1 + 5 = 6 v=1+5=6 v = 1 + 5 = 6 ), items 2, 4 (w = 3 + 5 = 8 > 7 w=3+5=8 > 7 w = 3 + 5 = 8 > 7 ). Optimal: items 2 and 3 (w = 3 + 4 = 7 w=3+4=7 w = 3 + 4 = 7 , v = 4 + 5 = 9 v=4+5=9 v = 4 + 5 = 9 ).
Problem. Given strings s s s of length m m m and t t t of length n n n Find the minimum number of insertions, deletions, and substitutions to transform s s s into t t t .
Recurrence:
d p [ i ] [ j ] = { j i f i = 0 i i f j = 0 d p [ i − 1 ] [ j − 1 ] i f s [ i ] = t [ j ] 1 + min ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] , d p [ i − 1 ] [ j − 1 ] ) i f s [ i ] ≠ t [ j ] dp[i][j] = \begin{cases} j & \mathrm{if} i = 0 \\ i & \mathrm{if} j = 0 \\ dp[i-1][j-1] & \mathrm{if} s[i] = t[j] \\ 1 + \min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) & \mathrm{if} s[i] \neq t[j] \end{cases} d p [ i ] [ j ] = ⎩ ⎨ ⎧ j i d p [ i − 1 ] [ j − 1 ] 1 + min ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] , d p [ i − 1 ] [ j − 1 ]) if i = 0 if j = 0 if s [ i ] = t [ j ] if s [ i ] = t [ j ]
Where the three cases in the minimum are: delete from s s s Insert into s s s Substitute in s s s .
Time: O ( m n ) O(mn) O ( mn ) . Space: O ( m n ) O(mn) O ( mn ) (can be reduced to O ( min ( m , n ) ) O(\min(m,n)) O ( min ( m , n )) ).
Worked Example: Edit Distance Compute the edit distance between “kitten” and “sitting”.
Building the DP table:
Edit distance: d p [ 6 ] [ 7 ] = 3 dp[6][7] = 3 d p [ 6 ] [ 7 ] = 3 .
Transform: kitten → sitten (substitute k→s) → sittin (substitute e→i) → sitting (insert g).
Problem. Given matrices A 1 , A 2 , … , A n A_1, A_2, \ldots, A_n A 1 , A 2 , … , A n where A i A_i A i has dimensions p i − 1 × p i p_{i-1} \times p_i p i − 1 × p i Find the parenthesisation that minimises the total number of scalar multiplications.
Recurrence:
d p [ i ] [ j ] = { 0 i f i = j min i ≤ k < j ( d p [ i ] [ k ] + d p [ k + 1 ] [ j ] + p i − 1 p k p j ) i f i < j dp[i][j] = \begin{cases} 0 & \mathrm{if} i = j \\ \min_{i \leq k < j} (dp[i][k] + dp[k+1][j] + p_{i-1} p_k p_j) & \mathrm{if} i < j \end{cases} d p [ i ] [ j ] = { 0 min i ≤ k < j ( d p [ i ] [ k ] + d p [ k + 1 ] [ j ] + p i − 1 p k p j ) if i = j if i < j
Time: O ( n 3 ) O(n^3) O ( n 3 ) . Space: O ( n 2 ) O(n^2) O ( n 2 ) .
Proof of correctness. The optimal parenthesisation of A i ⋯ A j A_i \cdots A_j A i ⋯ A j splits at some position k k k : ( A i ⋯ A k ) ( A k + 1 ⋯ A j ) (A_i \cdots A_k)(A_{k+1} \cdots A_j) ( A i ⋯ A k ) ( A k + 1 ⋯ A j ) . The cost is the cost of the left subproduct plus the cost of the right subproduct plus the cost of multiplying the resulting matrices (p i − 1 ⋅ p k ⋅ p j p_{i-1} \cdot p_k \cdot p_j p i − 1 ⋅ p k ⋅ p j scalar multiplications). The optimal k k k minimises this total. ■ \blacksquare ■
Worked Example: Matrix Chain Multiplication Matrices: A 1 A_1 A 1 (10 × 30 10 \times 30 10 × 30 ), A 2 A_2 A 2 (30 × 5 30 \times 5 30 × 5 ), A 3 A_3 A 3 (5 × 60 5 \times 60 5 × 60 ). Dimensions: p = [ 10 , 30 , 5 , 60 ] p = [10, 30, 5, 60] p = [ 10 , 30 , 5 , 60 ] .
d p [ 1 ] [ 1 ] = d p [ 2 ] [ 2 ] = d p [ 3 ] [ 3 ] = 0 dp[1][1] = dp[2][2] = dp[3][3] = 0 d p [ 1 ] [ 1 ] = d p [ 2 ] [ 2 ] = d p [ 3 ] [ 3 ] = 0 .
d p [ 1 ] [ 2 ] = p 0 p 1 p 2 = 10 × 30 × 5 = 1500 dp[1][2] = p_0 p_1 p_2 = 10 \times 30 \times 5 = 1500 d p [ 1 ] [ 2 ] = p 0 p 1 p 2 = 10 × 30 × 5 = 1500 . Split at k = 1 k=1 k = 1 : ( A 1 A 2 ) (A_1 A_2) ( A 1 A 2 ) .
d p [ 2 ] [ 3 ] = p 1 p 2 p 3 = 30 × 5 × 60 = 9000 dp[2][3] = p_1 p_2 p_3 = 30 \times 5 \times 60 = 9000 d p [ 2 ] [ 3 ] = p 1 p 2 p 3 = 30 × 5 × 60 = 9000 . Split at k = 2 k=2 k = 2 : ( A 2 A 3 ) (A_2 A_3) ( A 2 A 3 ) .
d p [ 1 ] [ 3 ] dp[1][3] d p [ 1 ] [ 3 ] : Try k = 1 k=1 k = 1 : d p [ 1 ] [ 1 ] + d p [ 2 ] [ 3 ] + 10 × 30 × 60 = 0 + 9000 + 18000 = 27000 dp[1][1] + dp[2][3] + 10 \times 30 \times 60 = 0 + 9000 + 18000 = 27000 d p [ 1 ] [ 1 ] + d p [ 2 ] [ 3 ] + 10 × 30 × 60 = 0 + 9000 + 18000 = 27000 . Try k = 2 k=2 k = 2 : d p [ 1 ] [ 2 ] + d p [ 3 ] [ 3 ] + 10 × 5 × 60 = 1500 + 0 + 3000 = 4500 dp[1][2] + dp[3][3] + 10 \times 5 \times 60 = 1500 + 0 + 3000 = 4500 d p [ 1 ] [ 2 ] + d p [ 3 ] [ 3 ] + 10 × 5 × 60 = 1500 + 0 + 3000 = 4500 .
Minimum: d p [ 1 ] [ 3 ] = 4500 dp[1][3] = 4500 d p [ 1 ] [ 3 ] = 4500 Split at k = 2 k=2 k = 2 : ( A 1 ( A 2 A 3 ) ) (A_1(A_2 A_3)) ( A 1 ( A 2 A 3 )) .
Problem. Given sequences X = ( x 1 , … , x m ) X = (x_1, \ldots, x_m) X = ( x 1 , … , x m ) and Y = ( y 1 , … , y n ) Y = (y_1, \ldots, y_n) Y = ( y 1 , … , y n ) Find the LCS.
Recurrence:
d p [ i ] [ j ] = { 0 i f i = 0 o r j = 0 d p [ i − 1 ] [ j − 1 ] + 1 i f x i = y j max ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) i f x i ≠ y j dp[i][j] = \begin{cases} 0 & \mathrm{if} i = 0 \mathrm{ or} j = 0 \\ dp[i-1][j-1] + 1 & \mathrm{if} x_i = y_j \\ \max(dp[i-1][j], dp[i][j-1]) & \mathrm{if} x_i \neq y_j \end{cases} d p [ i ] [ j ] = ⎩ ⎨ ⎧ 0 d p [ i − 1 ] [ j − 1 ] + 1 max ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ]) if i = 0 or j = 0 if x i = y j if x i = y j
Time: O ( m n ) O(mn) O ( mn ) . Space: O ( m n ) O(mn) O ( mn ) (can be reduced to O ( min ( m , n ) ) O(\min(m,n)) O ( min ( m , n )) for the length only).
Proof of correctness. If x i = y j x_i = y_j x i = y j Any LCS of X [ 1.. i ] X[1..i] X [ 1.. i ] and Y [ 1.. j ] Y[1..j] Y [ 1.. j ] must include x i x_i x i So L C S = 1 + L C S ( X [ 1.. i − 1 ] , Y [ 1.. j − 1 ] ) \mathrm{LCS} = 1 + \mathrm{LCS}(X[1..i-1], Y[1..j-1]) LCS = 1 + LCS ( X [ 1.. i − 1 ] , Y [ 1.. j − 1 ]) . If x i ≠ y j x_i \neq y_j x i = y j The LCS either Excludes x i x_i x i or excludes y j y_j y j Giving the max of the two subproblems. ■ \blacksquare ■
Problem. Given coin denominations d 1 , … , d n d_1, \ldots, d_n d 1 , … , d n and a target amount M M M Find the minimum number of coins needed.
Recurrence:
d p [ c ] = { 0 i f c = 0 min i : d i ≤ c ( d p [ c − d i ] + 1 ) i f c > 0 dp[c] = \begin{cases} 0 & \mathrm{if} c = 0 \\ \min_{i: d_i \leq c}(dp[c - d_i] + 1) & \mathrm{if} c > 0 \end{cases} d p [ c ] = { 0 min i : d i ≤ c ( d p [ c − d i ] + 1 ) if c = 0 if c > 0
Time: O ( n M ) O(nM) O ( n M ) . Space: O ( M ) O(M) O ( M ) .
Proof of correctness. To make change for amount c > 0 c > 0 c > 0 The last coin used must be some d i ≤ c d_i \leq c d i ≤ c . The remaining amount is c − d i c - d_i c − d i And the optimal solution for c c c uses 1 + d p [ c − d i ] 1 + dp[c - d_i] 1 + d p [ c − d i ] coins. Taking the minimum over all valid d i d_i d i gives the optimal solution. ■ \blacksquare ■
Worked Example: Coin Change Denominations: 1 , 5 , 10 , 25 \\{1, 5, 10, 25\\} 1 , 5 , 10 , 25 . Target: M = 63 M = 63 M = 63 .
Bottom-up computation:
d p [ 0 ] = 0 dp[0] = 0 d p [ 0 ] = 0 d p [ 1..4 ] = d p [ c − 1 ] + 1 = c dp[1..4] = dp[c - 1] + 1 = c d p [ 1..4 ] = d p [ c − 1 ] + 1 = c (use pennies)d p [ 5 ] = min ( d p [ 4 ] + 1 , d p [ 0 ] + 1 ) = 1 dp[5] = \min(dp[4]+1, dp[0]+1) = 1 d p [ 5 ] = min ( d p [ 4 ] + 1 , d p [ 0 ] + 1 ) = 1 (use a nickel)d p [ 10 ] = min ( d p [ 9 ] + 1 , d p [ 5 ] + 1 , d p [ 0 ] + 1 ) = 1 dp[10] = \min(dp[9]+1, dp[5]+1, dp[0]+1) = 1 d p [ 10 ] = min ( d p [ 9 ] + 1 , d p [ 5 ] + 1 , d p [ 0 ] + 1 ) = 1 (use a dime)… d p [ 25 ] = 1 dp[25] = 1 d p [ 25 ] = 1 (use a quarter)d p [ 50 ] = 2 dp[50] = 2 d p [ 50 ] = 2 (use two quarters)d p [ 63 ] = min ( d p [ 62 ] + 1 , d p [ 58 ] + 1 , d p [ 53 ] + 1 , d p [ 38 ] + 1 ) dp[63] = \min(dp[62]+1, dp[58]+1, dp[53]+1, dp[38]+1) d p [ 63 ] = min ( d p [ 62 ] + 1 , d p [ 58 ] + 1 , d p [ 53 ] + 1 , d p [ 38 ] + 1 ) Working backwards: d p [ 63 ] = d p [ 38 ] + 1 = d p [ 13 ] + 2 = d p [ 3 ] + 3 = 6 dp[63] = dp[38] + 1 = dp[13] + 2 = dp[3] + 3 = 6 d p [ 63 ] = d p [ 38 ] + 1 = d p [ 13 ] + 2 = d p [ 3 ] + 3 = 6 .
Solution: 2 quarters + 1 dime + 3 pennies = 25 + 25 + 10 + 1 + 1 + 1 = 63 25 + 25 + 10 + 1 + 1 + 1 = 63 25 + 25 + 10 + 1 + 1 + 1 = 63 . 6 coins.
Problem. Given a sequence a 1 , … , a n a_1, \ldots, a_n a 1 , … , a n Find the length of the longest strictly increasing subsequence (not necessarily contiguous).
Recurrence: d p [ i ] = 1 + max d p [ j ] : j < i a n d a j < a i dp[i] = 1 + \max\\{dp[j] : j \lt i \mathrm{~and~} a_j \lt a_i\\} d p [ i ] = 1 + max d p [ j ] : j < i and a j < a i With d p [ i ] = 1 dp[i] = 1 d p [ i ] = 1 if no such j j j exists.
Time: O ( n 2 ) O(n^2) O ( n 2 ) . Space: O ( n ) O(n) O ( n ) .
Worked Example: Longest Increasing Subsequence Find the LIS of [ 10 , 22 , 9 , 33 , 21 , 50 , 41 , 60 , 80 ] [10, 22, 9, 33, 21, 50, 41, 60, 80] [ 10 , 22 , 9 , 33 , 21 , 50 , 41 , 60 , 80 ] .
d p [ 0 ] = 1 dp[0] = 1 d p [ 0 ] = 1 (just 10) d p [ 1 ] = d p [ 0 ] + 1 = 2 dp[1] = dp[0] + 1 = 2 d p [ 1 ] = d p [ 0 ] + 1 = 2 (10, 22) d p [ 2 ] = 1 dp[2] = 1 d p [ 2 ] = 1 (just 9) d p [ 3 ] = d p [ 1 ] + 1 = 3 dp[3] = dp[1] + 1 = 3 d p [ 3 ] = d p [ 1 ] + 1 = 3 (10, 22, 33) d p [ 4 ] = d p [ 0 ] + 1 = 2 dp[4] = dp[0] + 1 = 2 d p [ 4 ] = d p [ 0 ] + 1 = 2 (10, 21) d p [ 5 ] = d p [ 3 ] + 1 = 4 dp[5] = dp[3] + 1 = 4 d p [ 5 ] = d p [ 3 ] + 1 = 4 (10, 22, 33, 50) d p [ 6 ] = d p [ 4 ] + 1 = 3 dp[6] = dp[4] + 1 = 3 d p [ 6 ] = d p [ 4 ] + 1 = 3 (10, 21, 41) d p [ 7 ] = d p [ 5 ] + 1 = 5 dp[7] = dp[5] + 1 = 5 d p [ 7 ] = d p [ 5 ] + 1 = 5 (10, 22, 33, 50, 60) d p [ 8 ] = d p [ 7 ] + 1 = 6 dp[8] = dp[7] + 1 = 6 d p [ 8 ] = d p [ 7 ] + 1 = 6 (10, 22, 33, 50, 60, 80)
LIS length: 6.
Patience sorting approach (O ( n log n ) O(n \log n) O ( n log n ) ): Maintain piles. For each card, place on the leftmost pile whose top card is ≥ \geq ≥ the current card, or start a new pile on the right if no such pile exists. The number of piles equals the LIS length.