Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Number of ways to make change for amount N

I came across this problem:

http://www.geeksforgeeks.org/dynamic-programming-set-7-coin-change/

Given a value N, if we want to make change for N cents, and we have infinite supply of each of S = { S1, S2, .. , Sm} valued coins, how many ways can we make the change? The order of coins doesn’t matter.

For example, for N = 4 and S = {1,2,3}, there are four solutions: {1,1,1,1},{1,1,2},{2,2},{1,3}. So output should be 4. For N = 10 and S = {2, 5, 3, 6}, there are five solutions: {2,2,2,2,2}, {2,2,3,3}, {2,2,6}, {2,3,5} and {5,5}. So the output should be 5.

I came up with the solution:

// recurrence relation
count[N] = count[N-d] for all denomination <= N

Source code
-----------

public static int numWays(int N, int[] denoms) {
  if (N == 0)
     return 0;
  
  int[] ways = new int[N+1];
  ways[0] = 1;
  
  for (int i=1; i<=N; i++) {
     ways[i] = 0;
     for (int d : denoms) {
        if (d <= i) {
           ways[i] += ways[i-d];
        }
     }
  }
  
  return ways[N];
}

But this counts duplicates which have the same denominations but in different order. For example, if denominations = {1,2} and N=3, then it counts {1,1,1}, {2,1}, {1,2} which has duplicate entry {1,2}.

I see that the DP solution described in the link here avoids duplicates. I understand how the recurrence relation works and all but I am not able to understand how it is able to avoid the duplicates while my solution is not. Please explain the idea behind.

like image 321
NPE Avatar asked Sep 04 '14 03:09

NPE


People also ask

How do you calculate possible combinations for a coin problem?

Given denominations of 'N' coins and an amount, we need to calculate the maximum number of ways(or combinations) the given amount can be paid. We are also given an infinite supply of each coin. So, here the possible combinations are 2 + 2 + 3 = 7 (amount) and 2 + 5 = 7 (amount).


1 Answers

Let C(i, j) the number of ways to make the total i using coins of denominations S1, ..., Sj. Your code implements the following recurrence (ordered ways).

C(i, m) | i <  0 = 0
        | i == 0 = 1
        | i >  0 = sum_{j = 1}^m C(i - Sj, m)

The linked code implements a different recurrence (unordered ways).

C(i, j) | i <  0           = 0
        | i == 0           = 1
        | i >  0 && j <= 0 = 0
        | i >  0 && j >  0 = C(i - Sj, j) + C(i, j - 1)

The difference between the two codes is subtle: more or less just how the loops are nested. Yours adds all of the terms for i before moving on to i + 1, but the linked code adds the j term for each i, then the j + 1 term for each i, etc. As a result, when the linked code considers the possibility of using a denomination-Sj coin from subtotal i, it implicitly is considering only those solutions that continue with coins of denominations S1, ..., Sj, because the current total for i - Sj does not include the possibilities that use other coins.

like image 141
David Eisenstat Avatar answered Nov 12 '22 23:11

David Eisenstat