动态规划概念:
Programming - 在这里指用数学方法来根据子问题求解当前问题(通俗理解就是找到递推公式)
Dynamic - 指缓存上一步结果,根据上一步结果计算当前结果
简单理解:找出递推公式,将当前问题分解成子问题,分阶段进行求解。
求解过程中缓存子问题的解,避免重复计算。
例题:
分析:
题目是求凑成总金额有几种凑法,硬币面值为1时,只有1种凑法,当有1、2、5三种面值的硬币时,可得如下图结果:
假设有1、2两种面值的硬币时,凑成金额为4,可以继承上一次的凑法 + 找出(总金额 - 当前面值)的余额的凑法。
代码实现:
import java.util.Arrays;
import java.util.stream.IntStream;
/**
* 零钱兑换Ⅱ - 动态规划
* 凑成总金额有几种凑法?
*/
public class ChangeMakingProblemLeetcode518 {
/* 面值 0 1 2 3 4 5 总金额-背包容量
* 1 1 1 11 111 1111 11111
*
* 2 1 1 11 111 1111 11111
* 2 21 211 2111
* 22 221
*
* 5 1 1 11 111 1111 11111
* 2 21 211 2111
* 22 221
* 5
* 面值-物品重量
*
* if(放得下){
* dp[i][j] = dp[i - 1][j] + dp[i][j - coin];
* }else{ //放不下
* dp[i][j] = dp[i - 1][j];
* }
*
* */
public int change(int amount, int[] coins) {
int[][] dp = new int[coins.length][amount + 1];
for (int i = 0; i < coins.length; i++) {
dp[i][0] = 1;
}
for (int j = 1; j < amount + 1; j++) {
if(j >= coins[0]){
dp[0][j] = dp[0][j - coins[0]];
}
}
for (int i = 1; i < coins.length; i++) {
for (int j = 1; j < amount + 1; j++) {
if(j >= coins[i]){ //放得下
dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]];
}else{
dp[i][j] = dp[i - 1][j];
}
}
print(dp);
}
return dp[coins.length - 1][amount];
}
public static void main(String[] args) {
ChangeMakingProblemLeetcode518 leetcode = new ChangeMakingProblemLeetcode518();
int count = leetcode.change(5, new int[]{1, 2, 5});
System.out.println(count);
}
static void print(int[][] dp) {
// 打印18个连续的"-"字符,用于分隔
System.out.println("-".repeat(18));
// 创建一个对象数组,数组的元素是从0到dp[0].length的整数序列
Object[] array = IntStream.range(0, dp[0].length + 1).boxed().toArray();
// 使用printf方法格式化打印数组,每个元素占两个字符的宽度,右对齐
System.out.printf(("%2d ".repeat(dp[0].length)) + "%n", array);
// 遍历二维数组的每一行
for (int[] d : dp) {
// 将当前行的元素转换为对象数组
array = Arrays.stream(d).boxed().toArray();
// 使用printf方法格式化打印当前行的元素,每个元素占两个字符的宽度,右对齐
System.out.printf(("%2d ".repeat(d.length)) + "%n", array);
}
}
}
代码优化:
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1;
for (int coin : coins) {
for (int j = coin; j < amount + 1; j++) {
dp[j] = dp[j] + dp[j - coin];
}
}
return dp[amount];
}