12.零钱兑换
- 题目描述
给你一个整数数组 coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1
。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
示例 2:
输入:coins = [2], amount = 3
输出:-1
- 题目分析
因为每个背包可以放同一种物品多次,所以这是一道完全背包问题
动态规划五部曲
1.创立二维dp数组:dp[i][j]:总金额为j时,放入面额为coins[0-i]时最小硬币个数
2.初始化二维数组:
当i=0时,表示只可以放入coins[0]:当背包容量为j>=coins[0]时,表示可以放下coin[0],则有dp[0][j] = dp[0][j - coins[0]] + 1;//比背包容量为j-coins[i]多一枚硬币
当j<coins[0]表示放不下此硬币,则不存在可装满背包容量为j的硬币数,此时需要设立一个标识,用来处理-1的情况,这里设置为dp[0][j] = amount + 1,即dp[i][j]>amount时,返回-1
3.递推公式
装下coins[i]:dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - coins[i]] + 1);//注意是最小
装不下coins[i]:dp[i][j] = dp[i - 1][j];
4.遍历顺序:先物品,再背包
5.遍历dp数组:
public static void printArray(int[][] array) {for (int i = 0; i < array.length; i++) {for (int j = 0; j < array[0].length; j++) {System.out.print(array[i][j] + " ");}System.out.println();}}
- Java代码实现(二维数组)
public static int coinChange(int[] coins, int amount) {int[][] dp = new int[coins.length][amount + 1];for (int j = 1; j <= amount; j++) {if (j >= coins[0]) {dp[0][j] = dp[0][j - coins[0]] + 1;} else {dp[0][j] = amount + 1;}}for (int i = 1; i < coins.length; i++) {for (int j = 1; j <= amount; j++) {if (j < coins[i]) {dp[i][j] = dp[i - 1][j];} else {dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - coins[i]] + 1);}}}return dp[coins.length - 1][amount] > amount ? -1 : dp[coins.length - 1][amount];}
- 降阶处理
- 由于是一位和二维性质一样,可以将二维数组[i]的部分去掉即可
public static int coinChange(int[] coins, int amount) {int[] dp = new int[amount + 1];for (int j = 1; j <= amount; j++) {if (j >= coins[0]) {dp[j] = dp[j - coins[0]] + 1;} else {dp[j] = amount + 1;}}for (int i = 1; i < coins.length; i++) {for (int j = 1; j <= amount; j++) {if (j >= coins[i]) {dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);}}}return dp[amount] > amount ? -1 : dp[amount];}