Tags: "leetcode", "dynamic-programming", access_time 3-min read

Edit this post on Github

Coin Change

Created: September 7, 2019 by [lek-tin]

Last updated: September 10, 2019

You are given coins of different denominations and a total amount of money amount. Write a function to compute 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.

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

Note

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

Solution:

Java top-down version

if i > coin[j]:
    T[i]       =     min{T[i]        ,  1+T[i - coin[j]]};
    changes     not picking jth coin    picking the jth coin
class Solution {
    public int coinChange(int[] coins, int amount) {
        if (coins == null || coins.length == 0 || amount < 0) return -1;

        int[] mem = new int[amount+1];
        int infLimit = Integer.MAX_VALUE - 1;
        for (int i = 0; i <= amount; i++) {
            if (i == 0) mem[0] = 0;
            else
                mem[i] = infLimit;
        }
        // coins then amount or amount then coin both work.
        for (int i = 0; i < coins.length; i++) {
            int coin = coins[i];
            for (int j = 1; j <= amount; j++) {
                if (j >= coin) {
                    int without = mem[j - coin];
                    if (without + 1 < mem[j]) {
                        mem[j] = without + 1;
                    }
                }
            }
        }
        // return Integer.MAX_VALUE;
        return mem[amount] == infLimit ? -1 : mem[amount];
    }
}

Solution (bottom-up dynamic programming)

Python bottom-up version
Time complexity : O(amount * coin_denoms)
Space complexity : O(coin_denoms)

import math

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        n = amount + 1
        ans = [math.inf for i in range(n)]
        # for amount = 0, there is no solution
        ans[0] = 0

        for i in range(1, n):
            for coin in coins:
                # skip all the coins whose denomination is greater that the current amount
                if i - coin < 0:
                    continue
                #           coin excluded, coin included
                ans[i] = min(ans[i], ans[i - coin] + 1)

        return ans[amount] if ans[amount] != math.inf else -1

Solution (top-down memoized recursion)

Python top-down version

import math

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        if amount < 1:
            return 0

        dp = [0 for i in range(amount)]

        return self.helper(amount, coins, dp)

    def helper(self, remain, coins, mem):
        if remain < 0:
            return -1
        if remain == 0:
            return 0
        if mem[remain-1] != 0:
            return mem[remain-1]

        min = math.inf
        for coin in coins:
            # Get result for remain-coin
            res = self.helper(remain-coin, coins, mem)
            if (res >= 0 and res < min):
                # if res is a better choice, update min with res
                min = res+1
        # keep traversing: check (remain-1)
        mem[remain-1] = -1 if (min == math.inf) else min
        return mem[remain-1]