问题
- 零钱兑换
给你一个整数数组 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-change
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
方法一
以示例1为例:
dp[i]表示总金额为i时所需要的最少硬币数。
求dp[11]:
所以dp[11] = min(dp[11 - 1], dp[11 - 2], dp[11 - 5]) + 1;
即 dp[11] = min(dp[10], dp[9], dp[6]) + 1;
之后再求dp[10],dp[9],dp[6],同dp[11]求法相同。
逆着求
dp[0] = 0;
dp[1] = dp[0] + 1;面额为1的拼出
dp[2] = dp[0] + 1;面额为2的拼出
dp[3] = dp[2] + 1;面额为1和2的拼出
等等。
所以状态转移方程为:dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
代码
public int coinChange(int[] coins, int amount) {
int n = coins.length;
int[] dp = new int[amount + 1];
int max = amount + 1;
Arrays.fill(dp, max);//所有初始化最大值
dp[0] = 0;//0枚硬币0种拼法
for(int i = 1; i <= amount; i++){
for(int j = 0; j < n; j++){
if(i >= coins[j])//金额数得大于等于硬币面额才可以拼出
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
}
}
if(dp[amount] == max)
return -1;
return dp[amount];
}
}
方法二
思想是增加硬币种数
状态转移方程不变
class Solution {
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
int max = amount + 1;
Arrays.fill(dp, max);
dp[0] = 0;
for(int i = 0; i < coins.length; i++){
for(int j = coins[i]; j <= amount; j++){
dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
}
}
if(dp[amount] == max)
return -1;
return dp[amount];
}
}
这两种方法的区别在于for循环的位置。