Coin Change (#322 Leetcode)

This is one of the unbounded/unlimited knapsack problem. 
Knapsack is a kind of binary choice problem, if we have an array of [1, 2, 5], it will be a sort of deciding to take the element 1 or not. 

Problem Statement 

You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.

Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.


You may assume that you have an infinite number of each kind of coin.

 

Example 1:

Input: coins = [1,2,5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1

Example 2:

Input: coins = [2], amount = 3
Output: -1

Example 3:

Input: coins = [1], amount = 0
Output: 0

 

Constraints:

  • 1 <= coins.length <= 12
  • 1 <= coins[i] <= 231 - 1
  • 0 <= amount <= 104


Greedy Method:

My first idea was greedy as mentioned below, so I chose the largest coin first. 

On each iteration, numOfCoins would tell how many coins are required to sum up the given amount.

For example, if we take the coins as [1, 3, 4, 5] and the amount as 7.

1. As we are concerned about the minimum number of coins, I chose to start from the last coin which is 5.

2. The specificCoin required to make up the sum 7 is just 1. 

3. The balance would be 2.

4. As the next coins 4 & 3 is greater than balance amount, we skipped these two coins. 

5. The last coin is 1, the specificCoin required to make up the balance sum 2 is just 2. 

Totally the numOfCoins required to make up the sum is 3. But actually 2 coins are enough to make up 7. The coins are 3 & 4. So this is the counter example for this problem. 

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        
        if (amount < 0 || coins.size() < 1 || coins.size() > 12) {
            return -1;
        }

        for (int i = coins.size() -1; i >= 0; i--) {
           if (coins[i] < 1 || coins[i] > 2147483647) {
               return -1;
           }
        }

        int numOfCoins = 0;
        int balance = amount;

        sort(coins.begin(), coins.end());

        for (int i = coins.size()-1; i >= 0; i--) {
            //cout << " balance " << balance << " coin " << coins[i] << " numOfCoins " << numOfCoins << endl;
            if (coins[i] <= balance) {
                numOfCoins += balance/coins[i];
                int specificCoin = balance/coins[i];
                balance = balance - (coins[i] * specificCoin);
                //cout << " Inside balance " << balance << " coin " << coins[i] << " numOfCoins " << numOfCoins << endl;
            }
            
        }
        if (balance != 0) {
            return -1;
        }
        return numOfCoins;
    }
};

Test Cases:


[1, 2, 5]
27

[2, 5, 10, 1]
11

//return -1 for balance != 0
[2]
3

// if not sorted
[2, 5, 10, 1]
27

// Greedy won't work
[186, 419, 83, 408]
6249

// Greedy won't work
[1, 3, 4, 5]
7


TopDown Method:

DFS/Backtracking/Bruteforce:

It is really a kind of Bruteforce approach, and I tried to to do this DFS backtracking method, but I couldn't finish it.

Here is the code

The time complexity is O(n^2) 

BottomUp Method:

Dynamic Programming:

Why the dp array is initialised with amount + 1?

The amount will start from 0 to 7, so totally 8 as there is a constraint of amount 0 as well. 

Why dp[0] is initialised?

The minimum coins, we would get from amount 0 are 0.


class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
         
        if (amount < 0 || coins.size() < 1 || coins.size() > 12) {
            return -1;
        }
        for (int i = coins.size() -1; i >= 0; i--) {
           if (coins[i] < 1 || coins[i] > 2147483647) {
               return -1;
           }
        }

        vector<int> dp(amount+1, amount+1);
        dp[0] = 0;
        for (auto coin : coins) {
            for (int i = coin; i <= amount; i++) {
                dp[i] = min(dp[i], dp[i-coin] + 1);
            }
        }
        return dp[amount] > amount ? -1 : dp[amount]; 
   }
    
};


Dry Run of Algorithm

Here is the link to the whiteboard.


The time complexity is O(amount * n)

Dynamic Programming table is as follows



Reference:

https://www.youtube.com/watch?v=mBNrRy2_hVs

My MSc materials from University of Leicester

Comments