I’m trying to understand the coin change problem solution, but am having some difficulty.
At the Algorithmist, there is a pseudocode solution for the dynamic programming solution, shown below:
n = goal number
S = [S1, S2, S3 ... Sm]
function sequence(n, m)
//initialize base cases
for i = 0 to n
for j = 0 to m
table[i][j] = table[i-S[j]][j] + table[i][j-1]
This is a pretty standard O(n^2) algorithm that avoids recalculating the same answer multiple times by using a 2-D array.
My issue is two-fold:
- How to define the base cases and incorporate them in
table[][]as initial values - How to extract out the different sequences from the table
Regarding issue 1, there are three base cases with this algorithm:
if n==0, return 1if n < 0, return 0if n >= 1 && m <= 0, return 0
How to incorporate them into table[][], I am not sure. Finally, I have no idea how to extract out the solution set from the array.
We can implement a dynamic programming algorithm in at least two different approaches. One is the top-down approach using memoization, the other is the bottom-up iterative approach.
For a beginner to dynamic programming, I would always recommend using the top-down approach first since this will help them understand the recurrence relationships in dynamic programming.
So in order to solve the coin changing problem, you’ve already understood what the recurrence relationship says:
Such a recurrence relationship is good but is not that well-defined since it doesn’t have any boundary conditions. Therefore, we need to define boundary conditions in order to ensure the recurrence relationship could successfully terminate without going into an infinite loop.
So what will happen when we try to go down the recursive tree?
If we need to calculate
table[i][j], which means the number of approaches to changeiusing coins from type0toj, there are several corner cases we need to handle:1) What if
j == 0?If
j == 0we will try to solve the sub-problemtable(i,j-1), which is not a valid sub-problem. Therefore, one boundary condition is:2) What if
i - S[j] < 0?We also need to handle this boundary case and we know in such a condition we should either not try to solve this sub-problem or initialize
table(i-S[j],j) = 0for all of these cases.So in all, if we are going to implement this dynamic programming from a top-down memoization approach, we can do something like this:
In practice, it’s also possible that we use the value of
tablearrays to help track whether this sub-problem has been calculated before (e.g. we can initialize a value of -1 means this sub-problem hasn’t been calculated).Hope the answer is clear. 🙂