##Dynamic Programming Problems - Hints Sheet
These are the sample problems which are intended to provide a hands-on introduction to solving problems using Dynamic Programming.
- Coin Change Problem
- Optimum Path Problem
- Billboard Location Problem
- Additional Problem - DNA Sequence Alignment
###Problem 1 - Coin Change Problem
Objective: Given a amount
Dynamic Solution: Start at zero and iterate up to the target amount 'A'. Using the available coins, calculate the minimum number of coins to go from a previous value that can be reached with 1 of the available coins.
For each value, v, in the range from 1 to A then you pick the
Example:
This is a walk-through of a sequence of steps to illustrate how the final answer can be determined.
Given a set of coins with values of 1p, 2p, 4p, make the total of 5p.
Note: Don't forget to store the results of each value of
$f(v)$ as v increases from 1 to A to mimimise the number of calculations and avoid recursion.
Objective: Given a 2-D matrix of positive integers, find the path from the top-left corner to the bottom-right of the matrix where the elements on the path have the smallest total sum. You are only allowed to move either right or down (i.e. can't move up, to the left. or diagonally)
This is a sample grid with the optimum path highlighted in blue.
Hint: There are only 2 ways to get to the final square. Which option should be selected?
Dynamic Solution: In order to reach the final cell, we have to come from either the cell immediately above it or from the one to it's immediate left. In order to minimise the total sore we would choose which of these 2 options had the lowest score to reach it. We can use this logic to pick the best route to the preceeding cell, and so on back to the top-left corner.
We treat the origin matrix as a grid (called a) of width
We take the smallest value from
So the general formula to calculate the best value for a cell in
The only variations on this are in the first row where there is no row above and the first column where there is no column to the left. In these two cases you only have 1 possible preceding cell.
Worked Example:
Given the following
1 | 2 | 6 |
---|---|---|
3 | 4 | 5 |
9 | 7 | 3 |
Start by calculating the top row in the
So top row of
You can then use this first row to calculate the 2nd row in the
So 2nd row in
Objective: Suppose you’re managing construction of billboards on the M62 between Leeds & Manchester, a distance of M miles. The possible sites for advertising hoardings are given by numbers
Regulations imposed by the Department of Transport require that no two billboards be within
Calculate the maximum achievable revenue and the location of the billboards which generates this figure.
Dynamic Solution: For each location which has the potential of placing billboard there is a choice to either place or not place a billboard.
If we build a list of the maximum revenues at point
We can then build the list of
We want to find the best way to align 2 sequences of DNA to minimise the number of differences between corresponding characters. For each, character in the sequences you have the option of either using the given character or inserting a gap "$-$" which will allow the subsequent characters to align better.
For example, if we aligned the following 2 sequences ACGGTC and ATCGCC we could produce an optimal alignment as: >A-CGGTC >ATCG-CC
This would have an 'Edit Distance' of 3 since there are 3 positions (2, 5 & 6) where the characters in the 2 generated sequences are different. This distance is called the Levenshtein Distance and forms the basis of the Needleman-Wunsch Algorithm which determines the distance between 2 DNA or Protein sequences in bioinformatics.
This problem should build upon your solution to the Optimum Path problem with the following difference that allows an additional option to move diagonally Left & Down in a single step. It uses an insertion cost of 1 for each gap inserted and a substitution cost of one if a diagonal move is made when the 2 characters are different.
When using Dynamic Programming I have found that in many cases it is useful to have a function which fills a void between the typical
import operator
def accumulate(iterable,
firstFn=lambda x:x,
subsequentFn=operator.add):
it = iter(iterable)
try:
previous = firstFn(next(it))
except StopIteration:
return
yield previous
for item in it:
previous = subsequentFn(item, previous)
yield previous```
This function is supplied out-of-the-box in Python 3 and the code above is my implementation in Python 2.
The
- An iterator containing the individual key items
- A function to be applied to derive the initial value for 1st item in the iterator - Defaults to the identity function.
- A function to be applied for each subsequent item in the iterator which takes the item value and the previous summary value to calculate the next value in the results list. This defaults to the addition function.
If the
Supplying multiplier operation instead of default addition produces: