题目描述
难度:中等
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
示例 2:
输入:coins = [2], amount = 3
输出:-1
示例 3:
输入:coins = [1], amount = 0
输出:0
示例 4:
输入:coins = [1], amount = 1
输出:1
示例 5:
输入:coins = [1], amount = 2
输出:2
提示:
1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104
分析
本题可以使用动态规划来完成,不得不说,动态规划刚开始学会一点皮毛的时候想用在题目上是真的难;
定义状态,dp[i] 表示总金额为 i 的情况下,需要的硬币最小数量;
初始化值 dp[0] = 0,总金额为 0 的时候不用消耗一枚硬币;
题目给定 coins 代表所有面值的硬币,我们用 coin 来代表 coins 中任意一面值;
对于总金额 i 来说,当 i >= coin 时,有dp[i] = min(dp[i - coin]) + 1
也就是说,dp[i] 是从所有只需要一枚硬币就可以达到 i 这个金额的金额数中取需要硬币数最少的金额;
举例例子解释一下,有 coins {1,2,3},amount = 6,当我们计算 dp[6] 的时候,是取 dp[6-1] dp[6-2] dp[6-3] 这三个值中最小的为结果然后加 1 就得到了 dp[6] 的所需要的硬币个数;
因为上面这三个值都只需要一枚硬币就可以达到 6 这个金额,而显而易见 dp[3] 需要的硬币最少,它只需要一枚,所以dp[6] 所消耗的硬币 是 dp[3] + 1 = 2;
所以有状态转移方程 dp[i] = min(dp[i - coin]) + 1
,coin ∈ coins;
代码:
public static int coinChange(int[] coins, int amount) {
if(amount == 0){
return 0;
}
int[] dp = new int[amount + 1]; // 状态
for (int i = 1; i < dp.length; i++){
dp[i] = Integer.MAX_VALUE - 1; // 定义每个金额所需硬币初始值
for (int coin : coins) {
if(i >= coin){ // 当满足 i >= coin 时,才能使用用该硬币
dp[i] = Math.min(dp[i - coin] + 1 , dp[i]);
}
}
}
return dp[amount] > amount ? -1 : dp[amount]; // 当硬币初始值未改变时,代表该硬币不能凑齐
}
总结
动态规划真难,就算写出来了,还是一愣一愣的,总感觉有点懵,特别是状态转移方程真的难想,再接再厉吧,每日充电!
岁月悠悠,衰微只及肌肤;热忱抛却,颓废必致灵魂