Friday, August 11th

 Coin Combinations

Given an array of coin values and an amount, return the combinations of coins that make up the amount

Photo courtesy of Michael Sander via Wikipedia.  Released under the Creative Commons 2.5 Generic license. No changes made.

This problem is a fundamental dynamic programming question akin to the knapsack problem.  Dynamic programming is apparent for this problem because solving brings many cases of overlapping subproblems.

We will attack this DP problem top down.

First we define our memoization structure, memo.  This requires identifying the state variables.  The state depends on 2 variables, the coin type being used, and the amount remaining.  So our memo will be 2-dimensional, based on number of coins and amount owed.  Values are initialzed to -1.  We also initialize coins as a global variable.  With memo and coins global, our recursive function will only require 2 variables, the state variables.

    vector<vector<int>> memo;
    vector<int> coins;

    int change(int amount, vector<int>& coins) {
        int numcoins=coins.size();
        this->coins=coins;
        memo = vector<vector<int>>(numcoins,vector<int>(amount+1,-1));
        return dp(numcoins-1,amount);
    }

Next we define our recursive function dp().  It takes an index to coins and an amount and returns the number of ways to make the amount

    int dp(int i, int amount){
        ...
    }

Within the recursive function, we first check to see if this particular subproblem has been solved already

    if( memo[i][amount]!=-1) return memo[i][amount];

Next we check the base case.  If we get to the last coin, of index 0, we test if only that coin can reach the final amount.  If the amount is divisible by the coin denomination, then we return 1, because there's only 1 way to make that amount with the single given coin.  Otherwise, if the amount is not divisible by the coin, we return 0;

    if(i==0return (amount%coins[i]==0);

Once the test and base case are addressed, we approach the recurrence relation.

For a given coin index and amount, we can either "not take" the coin, or "take" the coin.

To not take the coin, means we decrement the coin index while keeping the amount the same

    int nottake=dp(i-1,amount);

To take the coin depends on the amount.  We initialize it to zero.  If the current amount we're solving for is greater than the amount of the coin, we can decrement the amount by the value of the coin being taken.  Otherwise, if the amount remaining is less than the value of the coin, there is 0 ways to make that total with the given coin.

    int take=0;
    if(coins[i]<=amount){
      take=dp(i,amount-coins[i]);
    }

So, for a given coin, we can take or not take it.  The total number of ways to make an amount is by combining taking and not taking

    return dp[i][amount]=take+nottake;

There you have it, a top-down solution to the knapsack.  One of the hardest parts is just determining the state variables.

Thanks to user himanshu_mehra for a well organized recursive solution

Comments

Popular posts from this blog

Wednesday, September 20th

Monday, September 11th

Tuesday, September 19th