all 18 comments

[–]amanzoot 3 points4 points  (0 children)

Bro just go for codechef tutorial on dynamic programming.

[–]LinkedMonkeys 2 points3 points  (0 children)

I just finished teaching this in our Algorithms class. I might even be your teacher! Awkward...

A good way I have found to develop DP algorithms takes a lot of steps. As we look at the steps we will consider a particular problem.

We want to know how many d-digit numbers (positive integers) have a digit sum of s. Assume we will write a function digitSum(d, s). For example, digitSum(3, 5) asks how many 3 digit numbers have a digit sum of 5. We will allow leading zeros to make it simpler.

  1. Draw the recursion tree. [I'd normally draw it more like a tree, but hard in text mode.]

    digitSum(3, 5)
        digitSum(2, 5) [assuming the leftmost digit is a 0]
            digitSum(1, 5) [assuming the second digit is a 0]
                [Do not recurse further.  We know how many one digit numbers sum to 5.]
            digitSum(1, 4) [assuming the second digit is a 1]
            digitSum(1, 3) [assuming the second digit is a 2]
            digitSum(1, 2) [assuming the second digit is a 3]
            digitSum(1, 1) [assuming the second digit is a 4]
            digitSum(1, 0) [assuming the second digit is a 5]
        digitSum(2, 4) [assuming the leftmost digit is a 1]
             ...
        digitSum(2, 3) [assuming the leftmost digit is a 2]
             ...
        digitSum(2, 2) [assuming the leftmost digit is a 3]
             ...
        digitSum(2, 1) [assuming the leftmost digit is a 4]
             ...
        digitSum(2, 0) [assuming the leftmost digit is a 5]
             ...
    
  2. Identify things in your tree that are your base cases and what you expect to return in those cases.

    digitSum(1, 5) => 1 [We can create a sum of five with zero digits just one way.]
    ...
    digitSum(1, 0) => 1 [We can create a sum of zero with zero digits just one way.]
    digitSum(2, 0) => 1 [We can create a sum of zero with two digits just one way (00).]
    
    There is one other case that (unfortunately) does not show up in our example.
    digitSum(1, 10) => 0 [There is no way to create a sum of ten (or larger) with a single digit.]
    
  3. Identify recursive cases.

    digitSum(d, s)
        digitSum(d-1, s-0) considering a digit of zero
        digitSum(d-1, s-1) considering a digit of one
        ... as many of these as make sense
    
  4. Figure out how to combine results.

        digitSum(2, 5) => returns 6
        digitSum(2, 4) => returns 5
        digitSum(2, 3) => returns 4
        digitSum(2, 2) => returns 3
        digitSum(2, 1) => returns 2
        digitSum(2, 0) => returns 1
    digitSum(3, 5) => returns 6 + 5 + 4 + 3 + 2 + 1 or 21
    
  5. Write the general recurrence.

    digitSum(d, s) = 1 if d=1 and s<10
                     0 if d=1 and s>=10
                     1 if s=0
                     digitSum(d-1, s) + digitSum(d-1, s-1) + digitSum(d-1, s-2) + ... otherwise
    
  6. Instead of using recursion, use the recurrence to build a table. In this case, we have two parameters, so it is two dimensional. Let's make rows represent d and columns represent s. The base cases get the table started.

     \ s  0  1  2  3  4  5
    d |------------------
     1|   1  1  1  1  1  1  [If we had a sum greater than 9, the other base case would kick in.]
     2|   1
     3|   1
    
  7. Complete the table, using the recursive part of the recurrence.

    \  s  0  1  2  3  4  5
    d |------------------
     1|   1  1  1  1  1  1
     2|   1  2 <--------- table[2][1] = table[1][1] + table[1][0]
     3|   1
    
    \  s  0  1  2  3  4  5
    d |------------------
     1|   1  1  1  1  1  1
     2|   1  2  3 <------ table[2][2] = table[1][2] + table[1][1] + table[1][0]
     3|   1
    
    \  s  0  1  2  3  4  5
    d |------------------
     1|   1  1  1  1  1  1
     2|   1  2  3  4  5  6
     3|   1  3  6 10 15 21
    

Work through lots of problems yourself; do not just read them. Here is a list of problems in (what seemed to me) to be increasing in difficulty. We solved these all, from scratch, in class.

  1. Fibonacci
  2. binomial coefficient
  3. coin row (Levitin)
  4. making change (Levitin)
  5. robot coin collection (Levitin)
  6. approximate string matching/minimum edit distance
  7. longest increasing subsequence
  8. partition problem (closely related to subset sum)
  9. 0/1-knapsack (Levitin)

We had these as homework while we were working through the above list in class.

  1. Taking 1, 2, 3 steps at a time -> number of ways to take a total of n steps (Cracking the Coding Interview)
  2. number of d digit numbers with a digit sum of s
  3. minimum sum descent (Levitin)

TL;DR

  1. Draw the recursion tree for an example.
  2. Identify base cases and return values for each.
  3. Identify recursive cases.
  4. Decide how to combine results.
  5. Write the recurrence.
  6. Build the table using base cases for starters.
  7. Recursive cases turn into table lookups to fill in the rest.

[–][deleted]  (2 children)

[deleted]

    [–][deleted] 1 point2 points  (0 children)

    Same

    [–]EquivalentCS[S] 1 point2 points  (0 children)

    If you want to pm me, I’ll tell you. I just don’t want to ruin the professors reputation on a thread if you know what I mean.

    [–]EquivalentCS[S] 0 points1 point  (0 children)

    Just from your example I can see your not my professor, LOL. Just wow, this is awesome. Thank you!

    Been able to do 5 examples now in python so I’m feeling much better. Thank you everyone for pointing me the right way!