题目描述:
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2], amount = 3
输出: -1
分析:
首先明确条件,每一个硬币是可以多次使用的,通过选择来达到最优解。其实这个问题可以转换成那种爬楼梯的问题,这样的话更容易理解 如:我要达到amout层阶梯。我每次走一次的选择有(coins {1,2,5})。那么我最少需要走多少次呢。那么这个就涉及每一步的取舍问题。
解法一:dfs递归
我们可以 使用循环递归的方式:递归选择coins{1,2,5}。这里我们可以做一个技术处理 就是将coins进行排序,然后从最大的开始。(条件是剩余可走的台阶数量大于等于,我们这次所选择的走法。)。然后每次递归时,就可以 选或者不选,这样递归下去。比如{1,2,5}中,选5进入递归、不选进入一次递归。然后每次新进入的递归。都有两种选择,选择当走法,不选择当走法。 然后当正好走完时,记录走的次数。取出最小的即可。具体的代码也就不展示了,思路明白即可。
解法二:动态规划
定义DP[i]:表示,当凑出总金额为i的时候,所需要的最小银币数量(或者:当需要到低i个台阶上时,所需最小走的次数)。
那么动态转移方程DP[i]该如何表示呢?。是不是应该等于min{ DP[i] - coins[ j ]} + 1 ,也就是上一次选择之前的最小DP[i] 加上所选的这次
package leetcode;
import org.junit.Test;
import java.util.Arrays;
/**
* @author liuzihao
* @create 2019/12/22-21:38
* 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。
* 如果没有任何一种硬币组合能组成总金额,返回 -1。
*/
public class Demo322 {
@Test
public void test(){
//得出动态方程定义DP[i]:总金额为amout时的最小coins数量
//结果集合 其实也就是动态方程中的一个解
// J: 0-N coins[n]
// DP[I] = MIN { DP[I -COINS[J]] }从而可以推导出
int [] coins = {1, 2, 5};
int amount = 11;
System.out.println(coinChange(coins,amount));
}
// 输入: coins = [1, 2, 5], amount = 11
public int coinChange(int[] coins, int amount) {
int [] dp = new int[amount+1];
Arrays.fill(dp,1,dp.length,amount+1);//设置默认值,用于最后检验是否有解
dp[0] = 0; //初始化0 表示当需要0个总数的时候 有0总解法
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.length; j++){
if (i-coins[j] >= 0){
dp[i] = Math.min(dp[i-coins[j]] + 1 ,dp[i]);
}
}
}
// 当amount 为 amount+1 的时候 , 也就是没有赋值 此时无解 返回-1
return dp[amount]>amount ? -1:dp[amount];
}
}