题解:python
这是动态规划问题,动态规划问题通常用来求最值;web
动态规划三要素:算法
状态:当前的金额数量
状态转移方程:当前金额是n,至少须要dp[n]个硬币凑出当前金额;
最优子结构:若是当前金额n=0,则返回0;若是当前金额n<0, 则返回-1;svg
暴力递归法:(自顶向下)
递归终止条件:ui
代码以下:spa
class Solution: def coinChange(self, coins, amount): def dfs(n): ##递归终止条件 if n == 0: return 0 if n < 0: return -1 res = float("inf") for i in range(len(coins)): sub_problem = dfs(n - coins[i]) #子问题无解,跳过 if sub_problem == -1: continue #res记录子问题的最优解 res = min(res, 1 + sub_problem) if res == float("inf"): return -1 else: return res return dfs(amount)
可是上述方法会引入冗余计算;
以下图所示:
备忘录算法:
创建一个字典记录已经计算过的金额,避免重复计算;code
class Solution: def coinChange(self, coins, amount): memo = dict() #备忘录算法 def dfs(n): if n in memo:return memo[n] if n == 0: return 0 if n < 0: return -1 res = float("inf") for coin in coins: sub_problem = dfs(n - coin) #子问题无解,跳过 if sub_problem == -1: continue #res记录子问题的最优解 res = min(res, 1 + sub_problem) if res == float("inf"): return -1 else: memo[n] = res return res return dfs(amount)
自底向上的算法:
问题分解:
参考自leetcode的官方题解:
我的理解:
对于当前的金额i,须要遍历每个硬币,来找i-coins的最小值;以总金额为2,coins=[1,2]为例;
F(2) = min(F(2-1),F(2-2)) + 1 = 1
代码以下,两层循环实现;第一层循环对金额进行遍历,求出每个金额的最优硬币数量;
第二层,遍历每个硬币,找到当前金额时须要的最小硬币数;但在进行循环以前,须要判断当前金额和硬币的大小关系,不然会出现金额小于硬币的状况,产生负数;xml
class Solution: def coinChange(self, coins, amount): dp = [amount + 1]*(amount + 1) dp[0] = 0 for i in range(1,amount + 1): for j in range(len(coins)): if i >= coins[j]: dp[i] = min(dp[i], dp[i - coins[j]] + 1) if dp[amount] == amount + 1: return -1 else: return dp[amount]
这个代码和上一个代码思想一致,可是循环倒是相反的;
下面这个代码是先求出在当前的一个硬币下,dp[i]的值,此时在一个硬币循环完成后,获得的dp[i]并非最优的,而是在遍历每个硬币的过程当中,求出dp[i]的最优值;
例如金额为2,coins为[1,2]时;
硬币为1时,dp[1] = min(dp[1],dp[0] + 1) = 1, dp[2] = min(dp[2],dp[2-1] + 1) = 2;
硬币为2时,___________________________dp[2] = min(dp[2],dp[2-2] + 1) = 1
同时,下述代码在第二层循环中不用判断金额和当前硬币的大小(由于其是从当前硬币的金额大小开始进行循环的);而在第一个代码中须要判断,由于金额是从1开始循环;所以,第二个的时间复杂度是小于第一个的,效率更高。blog
class Solution: def coinChange(self, coins, amount): dp = [amount + 1]*(amount + 1) dp[0] = 0 for i in range(len(coins)): for j in range(coins[i], amount + 1):#避免了用if条件语句 dp[j] = min(dp[j], dp[j - coins[i]] + 1) if dp[amount] == amount + 1: return -1 else: return dp[amount]