给定不同面额的硬币 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
注意第一次搜索到了答案不一定是最优的,之后还要继续搜索
if((amount-sum)/coins[i]+count>=ans) break;//关于count的剪枝操作,属于优化操作,即剩下的重量全部装coins[i]的个数再加上已经的count必须小于ans,这里的ans是已经搜索过的答案。
if(sum+coins[i]<=amount)//关于sum的剪枝操作,属于必须操作
class Solution {
int[] coins;
int amount;
int ans=0x7fffffff;
public int coinChange(int[] coins, int amount) {
this.coins=coins;
this.amount=amount;
Arrays.sort(coins);
dfs(coins.length-1,0,0);
return ans==0x7fffffff?-1:ans;
}
void dfs(int index,int sum,int count){
if(sum==amount){
//if(count<ans)
ans=count;
return ;
}
for(int i=index;i>=0;i--){
//比if(count<ans)判断条件要好很多
if((amount-sum)/coins[i]+count>=ans) break;//关键的判断语句)
if(sum+coins[i]<=amount)
dfs(i,sum+coins[i],count+1);
}
}
}
class Solution {
int[] coins;
int amount;
int ans=0x7fffffff;
boolean flag=false;
public int coinChange(int[] coins, int amount) {
this.coins=coins;
this.amount=amount;
Arrays.sort(coins);
dfs(coins.length-1,0,0);
return ans==0x7fffffff?-1:ans;
}
void dfs(int index,int sum,int count){
if(amount==sum){
ans=count;
return ;
}
if(index<0) return ;
if((sum+coins[index]<=amount) && ((amount-sum)/coins[index]+count<ans)){
dfs(index,sum+coins[index],count+1);
}
dfs(index-1,sum,count);
}
}
动态规划一维数组
public class Solution {
public int coinChange(int[] coins, int amount) {
int max = amount + 1;
int[] dp = new int[amount + 1];
Arrays.fill(dp, max);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.length; j++) {
if (coins[j] <= i) {
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
}
}
}
return dp[amount] == max ? -1 : dp[amount];
}
}
动态规划二维数组:跟背包问题很像,但是要注意,这里每个数字可以重复,所以是
dp[i][j - coins[i - 1]] + 1,背包问题是dp[i-1][j - coins[i - 1]] + 1
class Solution {
public int coinChange(int[] coins, int amount) {
int nums = coins.length;
int[][] dp = new int[nums + 1][amount + 1];//dp[i][j]代表前i个***的钱加起来为j最少个数
for (int i = 0; i < dp.length; i++) {
Arrays.fill(dp[i], amount+1);
if (i > 0) dp[i][0] = 0;
}
for (int i = 1; i <= nums; i++) {
for (int j = 1; j <= amount; j++) {
if (j < coins[i - 1]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - coins[i - 1]] + 1);
}
}
return dp[nums][amount]== amount+1? -1:dp[nums][amount];
}
}