学习目标:

动态规划五部曲: ① 确定dp[i]的含义 ② 求递推公式 ③ dp数组如何初始化 ④ 确定遍历顺序 ⑤ 打印递归数组 ---- 调试 引用自代码随想录!

60天训练营打卡计划!

学习内容:

完全背包问题 – 二维dp数组

动态规划五步曲: ① 确定dp[i][j]的含义 : 任取[0, i]的物品(可重复使用)后放进容量为j的背包 所能放的 最大价值 ② 求递推公式 : dp[i][j] = Math.max(dp[i-1][j], dp[i][j-weight[i]] + value[i]); Ⅰ 不放物品 i : dp[i][j] = dp[i-1][j]; Ⅱ 放物品 i : dp[i][j-weight[i]] + value[i] 依赖本行已填充的值 ③ dp数组如何初始化 : 对第一行进行单独的初始化,如下图所示。当背包容量大于第一个物品的重量时开始放入背包,一直连续放入。 ④ 遍历顺序:先遍历物品,再遍历背包,和01背包问题一致。

下面的代码应该是没问题的,但是数字量变大之后会很容易超时。

// 动态规划

import java.util.Scanner;

public class Main {

public static void main(String[] args) {

//m,n分别代表物品种类和背包容量

int itemSize = 0,bagSize = 0;

Scanner sc = new Scanner(System.in);

//获取itemSize和bagSize的值

itemSize = sc.nextInt();

bagSize = sc.nextInt();

//初始化对应的重量数组和价值数组

int[] weight = new int[itemSize];

int[] value = new int[itemSize];

//这两个都是物品的属性,大小只和物品数量有关

for(int i = 0;i < itemSize;i++){

weight[i] = sc.nextInt();

value[i] = sc.nextInt();

}

// 完全背包问题

testWeightBagProblem(weight,value,bagSize);

}

/**

* 动态规划获得结果

* @param weight 物品的重量

* @param value 物品的价值

* @param bagSize 背包的容量

*/

public static void testWeightBagProblem(int[] weight, int[] value, int bagSize){

int itemSize = weight.length;

// dp数组的含义是:在[0,i]件物品中选择是否放入背包 的 最大价值

int[][] dp = new int[itemSize][bagSize+1];

int val = 0;

// 初始化dp数组,默认都为0.

// 使用最小重量的物品为该物品所在行赋值

for(int j = weight[0]; j < bagSize+1; j++){

if(j % weight[0] == 0) val += value[0];

dp[0][j] = val;

}

// 正常的为dp数组赋值,依赖左上位置的其他的dp值

for(int i = 1; i < itemSize; i++){

// j是背包容量

for(int j = 1; j < bagSize+1; j++){

// 如果容量可以放入新的物品,则从本行的左侧继承

if(j >= weight[i]){

// 一定要填j < weight[i]的位置,

// dp[i][j-weight[i]]与该行左侧的值息息相关。

dp[i][j] = Math.max(dp[i-1][j], dp[i][j-weight[i]] + value[i]);

}

// 如果容量不够放入新的物品,则从上一行继承

else if(j < weight[i]) dp[i][j] = dp[i-1][j];

}

}

System.out.println(dp[itemSize-1][bagSize]);

// 打印dp数组

for (int i = 0; i < itemSize; i++) {

for (int j = 0; j <= bagSize; j++) {

System.out.print(dp[i][j] + "\t");

}

System.out.println("\n");

}

}

}

完全背包问题 – 一维dp数组

动态规划五步曲: ① 确定dp[j]的含义 : 任取[0, i]的物品(可重复使用)后放进容量为j的背包 所能放的 最大价值 ② 求递推公式 : dp[j] = Math.max(dp[j], dp[j-weight[i]] + value[i]); Ⅰ 不放物品 i : dp[j] Ⅱ 放物品 i : dp[j-weight[i]] + value[i] ③ dp数组如何初始化 : 初始化为0 ④ 遍历顺序:先遍历物品,再遍历背包(正序),和01背包问题8同。

虽然下面的代码应该是没问题的,但是数字量变大之后会很容易超时。

import java.util.Scanner;

public class Main {

public static void main(String[] args) {

//m,n分别代表物品种类和背包容量

int itemSize = 0,bagSize = 0;

Scanner sc = new Scanner(System.in);

//获取itemSize和bagSize的值

itemSize = sc.nextInt();

bagSize = sc.nextInt();

//初始化对应的重量数组和价值数组

int[] weight = new int[itemSize];

int[] value = new int[itemSize];

//这两个都是物品的属性,大小只和物品数量有关

for(int i = 0;i < itemSize;i++){

weight[i] = sc.nextInt();

value[i] = sc.nextInt();

}

// 完全背包问题

testWeightBagProblem(weight,value,bagSize);

}

/**

* 动态规划获得结果

* @param weight 物品的重量

* @param value 物品的价值

* @param bagSize 背包的容量

*/

public static void testWeightBagProblem(int[] weight, int[] value, int bagSize){

int itemSize = value.length;

int[] dp = new int[bagSize+1];

// 初始化为0

// 递归推导

for(int i = 0; i < itemSize; i++){

for(int j = weight[i]; j < bagSize+1; j++){

dp[j] = Math.max(dp[j], dp[j-weight[i]] + value[i]);

// System.out.print(dp[j] + "\t");

}

// System.out.println("\n");

}

System.out.print(dp[bagSize] + "\n");

}

}

518.零钱兑换II

动态规划五步曲: ① 确定dp[i]的含义 : 装满容量为i的背包有dp[i]种方法。 ② 求递推公式 : dp[j] += dp[j - coins[i]] (装满背包的方法数的相关问题的统一递归公式) ③ dp数组如何初始化 : dp[0] = 1 (若dp[0] = 0,则所有的全部为0。) ④ 确定遍历顺序 : 从前向后,先物品后背包 ⑤ 打印dp数组

组合的dp数组

1 1 1 1 1

1 2 2 3 3

1 2 2 3 4

排列的dp数组

1 1 1

1 1 1

1 2 2

2 3 3

3 5 5

5 8 9

一种记忆方法:先遍历物品(物品不会重复被取得)为组合

class Solution {

public int change(int amount, int[] coins) {

int[] dp = new int[amount+1];

int itemSize = coins.length;

// 初始化

dp[0] = 1;

// 递归推导

for(int i = 0; i < itemSize; i++){

// 先物品后背包就可以保证是组合而不是排列

for(int j = coins[i]; j < amount+1; j++){

// 因为减去coins[i]的面值,就是可以凑出的方法数。

dp[j] += dp[j - coins[i]];

System.out.println(dp[j]);

}

System.out.println("*");

}

// for(int j = 0; j < amount+1; j++){

// // 先背包后物品就可以保证是排列而不是组合

// // for(int j = i; j < amount+1; j++){

// for(int i = 0; i < itemSize; i++){

// // 因为减去coins[i]的面值,就是可以凑出的方法数。

// if(j - coins[i] >= 0)

// dp[j] += dp[j - coins[i]];

// System.out.println(dp[j]);

// }

// System.out.println("*");

// }

return dp[amount];

}

}

377.组合总和IV

动态规划五步曲: ① 确定dp[i]的含义 : 装满容量为i的背包有dp[i]种方法(排列)。 ② 求递推公式 : dp[j] += dp[j - coins[i]] (装满背包的方法数的相关问题的统一递归公式) ③ dp数组如何初始化 : dp[0] = 1 (若dp[0] = 0,则所有的全部为0。) ④ 确定遍历顺序 : 从前向后,先背包后物品 ⑤ 打印dp数组

class Solution {

public int combinationSum4(int[] nums, int target) {

int[] dp = new int[target+1];

int itemSize = nums.length;

Arrays.sort(nums);

// 初始化

dp[0] = 1;

// 递归推导

for(int j = 0; j < target+1; j++){

// 先背包后物品就可以保证是排列而不是组合

for(int i = 0; i < itemSize && j >= nums[i] ; i++){

// 因为减去coins[i]的面值,就是可以凑出的方法数。

dp[j] += dp[j - nums[i]];

// System.out.println(dp[j]);

}

// System.out.println("*");

}

return dp[target];

}

}

学习时间:

上午两个半小时,整理文档半小时。

参考阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。