零钱兑换问题

虽然leetcode上将零钱兑换问题标记为中等难度,但我觉得这两个问题的难度比一些困难的问题还更让人摸不着头脑。

 

两个问题:分别是求兑换需要的最少硬币个数、兑换方式有几种。这两个问题相比之下,第一个问题更容易理解一些。

 

兑换需要的最少硬币个数

一开始看这个问题,先入为主的认为硬币时1、2、5,然后就直接想到了贪心算法。对于1、2、5的组合,贪心算法绝对是没问题的,因为只要能被大硬币cover的,一定是用大硬币更好,人们用1、2、5作为货币也是有道理的。但是,硬币的面值并不是普通方式,好吧,只能将问题泛化思考了。

给定的钱数a,硬币coins。依然是先考虑最大面值的硬币c[n-1],分别选择0、1、2... a/coins[n-1] 个,然后递归选择更小面值的硬币。这时最优子结构和状态数组就呼之欲出了,dp[k][i]代表 面值小于c[k]的所有硬币组合出钱数i 的最少使用硬币数。状态转移方程就是   dp[k][i] = min (dp[k-1][i-m*coins[k]]) m=0、1、2... a/coins[k]

上面是自顶向下的思考方式,下面再考虑自底向上的思考方式。

假设f(i) 为组合出钱数i的最少硬币数,那么用这个最优值分别加上一个硬币c组成的钱数i+c,f(i)+1应该也是组成i+c的最少硬币数的一种可能性。此时状态数组和状态转移方程也很容易想到了,用迭代就能实现。

还可以如此思考:假设现在要求f(i),假设f(i-1)之前都代表最少硬币数,f(i) = min (f(i-coins[k]) + 1) ,k = 0、1、2……n-1

还有用贪心+dfs算法的,基本上是通过剪枝来达到提高速度的,如这篇文章所提,我也不知道DFS为什么比dp快 。https://leetcode-cn.com/problems/coin-change/solution/dfsjian-zhi-2ms-ji-bai-100bi-dphuan-kuai-by-iejepw/

 

兑换方式有几种

这里的自底向上思考方式和上面问题已经不一样了,强行使用相同的方式,会发现计算结果会多出来。因为这个问题求的是组合数,而上面问题不在乎是组合还是排列,是找所有可能性(也可以称为排列吧)的最小值。

那么,重新从泛化的角度开始思考。

给定的钱数a,硬币coins。依然是先考虑最大面值的硬币c[n-1],分别选择0、1、2... a/coins[n-1] 个,然后递归选择更小面值的硬币。那么,这里就可以尝试获得最优子结构和状态数组了。用dp[k][i] 代表用小于等于coins[k]面值的硬币组合成钱数i的兑换方式,如果无法兑换,即为0。状态转移方程也可以得出:dp[k][i] = sum (dp[k-1][i - coins[k]]),k=0、1、2……a/coins[k]。递归方式实现即可,状态数组初始化为-1,计算过程中保存计算结果避免重复计算。

那么有没有自底向上的思考方式呢?有,leetcode官方的思路就是https://leetcode-cn.com/problems/coin-change-2/solution/ling-qian-dui-huan-ii-by-leetcode/。但是这个思路解释的不够清楚,有一篇文章说的比较好,而且解释了为什么这个问题是组合问题、上面的问题是排列问题。并且和爬楼梯问题进行了对比,写的相当精彩,值得一看 -> https://leetcode-cn.com/problems/coin-change-2/solution/ling-qian-dui-huan-iihe-pa-lou-ti-wen-ti-dao-di-yo/

 

 

 

你可能感兴趣的:(算法)