题目:给定不同面额的硬币coins和一个总金额amount,编写一个函数来计算凑成总金额的最少硬币数量,如果没有任何一种硬币可以组成总金额,则返回-1。
关于硬币问题我们可以有3种思路:
- 贪心算法:每次选取面值最大的硬币,但会存在鼠目寸光现象,无法达到最优效果,比如coins=[1,5,11],amount=15,求解amount=111+14,硬币数量为1+4=5(贪心法),但是15=5*3,硬币数量更少
- 暴力枚举,未尝试,想想就是for循环嵌套一大堆
- 动态规划–DP,特点:最优子结构,无后效性(本次硬币数量求最优解采用动态规划)
举个栗子┗|`O′|┛ 嗷~~!
coins=[1,2,3],amonunt=6,F(i)代表总金额为i的硬币数量,分析下面表格:
我们可以得出规律,总金额为i的最少硬币数量为:
F(i)=min(F(i-coins[j]))+1 //j为0到硬币数量-1
视频学习推荐:https://www.bilibili.com/video/av85797996
附官方解答方法三代码(自行添加的注释),方法实在是妙
public class leet322 {
public int coinChange(int[] coins, int amount) {
int max = amount + 1;//数组长度
int[] dp = new int[amount + 1];//数组初始化
Arrays.fill(dp, max);//创建数组,数组填充
dp[0] = 0;//面额为0的最优解为0
//开始遍历,i和j分别代表dp(i)行数(从1至总金额-1)和面额数量
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.length; j++) {
if (coins[j] <= i) {
//巧妙之处:每一行的第一个最小值必定为dp[i-coins[j]]+1
//因为第一个的dp[i]还没有确定,进行一行中的第二个面额求最优解
//将第一个的dp[i]与第二个的dp[i-coins[j]]+1进行比较
//依次得出dp[i]的最优解
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
}
}
}
//面额*数量=总金额,那dp[amount]一定小于等于总金额
return dp[amount] > amount ? -1 : dp[amount];
}
}