Now that we're familiar with Recursion (L1) and Python (L2), we'll look at a very important problem solving technique, DP. Just like Recursion = {Fairy, Idea}, DP = {Recursion, Memory}.

DP is NOT breaking problems into smaller subproblems and solving them. That's Recursion. DP is using extra memory to speed up recursion. This distinction clear, today we'll do loads of examples in DP.

Pointers:

  • Spillover from L2 (E18)
  • Discussion of Python Docs
  • Homework 1 on Sat (Python)
  • Time Complexity (big Oh)
  • Substring, Subset and Subsequence
  • Overview of Competitive in Python
  • DP as a space to time tradeoff option

E1. Fibonacci.

E2. Binomial Coefficients.

E3. S is a list of integers, find the longest increasing subsequence of S.

E4. Given a set of integers S = {s1..sn}, find (if exists) a subset that adds exactly to T (given).

E5. Take X,Y,Z as strings (where len(Z) = len(X) + len(Y)) and tell whether Z is a shuffle of X+Y.

HOMEWORK:

E6. Cut a rod (length n) into integer parts to maximize the total cost. Cost of parts is P = {P1..Pn}.

E7. T = {T1..Tn} are nos on a circle (some may be negative). Find the maximum possible sum of any arc.

In [29]:
# S1

def fibonacci(n):
    mem = []
    mem.append(0)         # f(0) = 0
    mem.append(1)         # f(1) = 1
    for i in range(n):
        mem.append(mem[-1] + mem[-2])
    return mem[-1]
In [30]:
print(fibonacci(42))
# notice the time speedup!
433494437
In [35]:
# S2 (with Pizzeria proof) (and why they're called BCs)

def bin_coff(n, r): 
    C = [[0 for _ in range(r+1)] for _ in range(n+1)] 
    for i in range(n+1): 
        for j in range(min(i, r) + 1): 
            if j == 0 or j == i:                         # base case 
                C[i][j] = 1
            else:                                        # recursion
                C[i][j] = C[i-1][j-1] + C[i-1][j] 
  
    return C[n][r] 
In [36]:
print(bin_coff(5,2))
10
In [51]:
# S3

def longest_increasing_subsequence(S):
    L = [1 for _ in range(len(S))]               # length of LIS ending at index
    P = [-1 for _ in range(len(S))]              # index of previous number in LIS
    for i in range(1, len(S)):
         for j in range(i):
            if (S[i] > S[j]) and (L[j]+1 > L[i]):
                L[i] = L[j]+1
                P[i] = j
                               
    def reconstruct(S, L, P):
        LIS = []
        t = max(range(len(L)), key = lambda z: L[z])
        while P[t] != -1:
            LIS.append(S[t])
            t = P[t]
        else:
            LIS.append(S[t])
        return LIS[::-1]
                               
    return reconstruct(S,L,P)
In [52]:
print(longest_increasing_subsequence([2,4,3,5,1,7,6,9,8]))
[2, 4, 5, 7, 9]
In [53]:
# S4 (Read up more about the Knapsack problem)

def knapsack(S, T): 
    n = len(S)
    pr = [ [False for i in range(T+1)] for j in range(n+1)]                     # partial results
    for i in range(n+1): 
        pr[i][0] = True                                                                           # base case
    for i in range(1,n+1): 
        for j in range(1,T+1):
            pr[i][j] = pr[i-1][j] if j<S[i-1] else (pr[i-1][j] or pr[i-1][j-S[i-1]])          # recursive step
    return pr[n][T] 
In [57]:
print(knapsack([1,2,5,9,10], 23))
print(knapsack([1,2,5,9,10], 22))
False
True
In [60]:
# S5

def is_shuffle(X, Y, Z):
    n = len(X)
    m = len(Y)
    assert(len(Z) == n+m)                                                     # precondition (more in L22)
    ps = [[False for i in range(m+1)] for j in range(n+1)]    
    for i in range(n+1):
        for j in range(m+1):
            if i == 0:                               
                ps[i][j] = (Z[:j] == Y[:j])                                 # base cases
            elif j==0:
                ps[i][j] = (Z[:i] == X[:i])
            else:
                ps[i][j] = ((ps[i-1][j] and Z[i+j-1]==X[i-1])) or ((ps[i][j-1] and Z[i+j-1]==Y[j-1]))
    return ps[n][m]
In [61]:
print(is_shuffle('chocolate', 'chips', 'cchocohilaptes'))
print(is_shuffle('chocolate', 'chips', 'chocochilatspe'))
True
False
In [ ]:
# S6
## HOMEWORK. Fill here and submit.
In [48]:
# S7
## HOMEWORK. Fill here and submit.

This lecture might have been a teensy bit hard. If that's the case, practice more, it'll get much easier with time.

On the other hand, if you're comfortable with this lecture, go on to read this.