pydata

Keep Looking, Don't Settle

leetcode 322. Coin Change

322. Coin Change

题目大意 给定一堆硬币和一个数字,求组成那个数字的最少硬币个数.如果币值没法构成给定数字,返回 -1

比如coins = [1, 2, 5], amount = 11,那么最少需要三枚硬币: (11 = 5 + 5 + 1)

解题思路:本题可以用动态规划和binary search来做。

动态规划:定义dp[i][j]表示用前i种硬币构成数字j的最少硬币个数。那么前i-1种硬币可以构成j-k * coin_i,都是合法的。所以dp[i][j] = min(dp[i][j], dp[i-1][j - k * coin_i]+ k ). 状态转移方程还有一个简化的版本:用前i个硬币构成j - coin_i,然后把coin_i加上就可以了。所以dp[i][j] = min(dp[i][j], dp[i][j - coin_i] + 1).

1. 动态规划,第一个写法

class Solution(object):
    def coinChange(self, coins, amount):
        dp = [float('inf') for _ in range(amount + 1)]
        dp[0] = 0
        for coin in coins:
            for i in range(amount-coin, -1, -1):
                if(dp[i] != float('inf')):
                    for k in range(1, amount + 1):
                        if k*coin + i <= amount:
                            dp[i+k*coin] = min(dp[i+k*coin], dp[i]+k)
        if dp[amount] < float('inf'):
            return dp[amount]
        else:
            return -1

2. 动态规划,第二种解法

class Solution(object):
    def coinChange(self, coins, amount):
        dp = [amount + 1 for _ in range(amount + 1)]
        dp[0] = 0
        for coin in coins:
            for i in range(coin, amount + 1):
                dp[i] = min(dp[i], dp[i - coin] + 1)
        if dp[amount] >= amount + 1:
            return -1
        else:
            return dp[amount]

3. dfs

第三种办法,dfs加强剪枝。首先把coins从大到小排列,先使用大的。如果剩余amount正好整除币值,就是一个合法解。

class Solution(object):
    def coinChange(self, coins, amount):
        coins = sorted(coins)[::-1]
        self.ans = float('inf')
        def dfs(coins, s, amount, count):
            coin = coins[s]
            # reach last coin
            if s == len(coins) - 1:
                if amount % coin == 0:
                    self.ans = min(self.ans, count + amount / coin)
            else:
                for k in range(amount/coin, -1, -1):
                    if count + k < self.ans:
                        # pruning, only search if new ans is less than current ans
                        dfs(coins, s + 1, amount - k*coin, count+k)           
        dfs(coins, 0, amount, 0)
        if self.ans < float('inf'):
            return self.ans
        else:
            return -1