322. 零钱兑换
题目讲解:代码随想录
重点:
- 理解递推公式及遍历顺序。
- 这题求的是最少个数,也就是不论组合还是排列都可以。
思路:
- dp数组的含义
// 凑成总金额为j的最少硬币个数 int[] dp = new int[amount + 1];
- 递推公式
// 不取当前硬币 或者 取当前硬币,看哪个方法个数最少 dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
- dp数组的初始化
// 凑成总金额为0的最少硬币个数肯定是0 dp[0] = 0; // 递推公式是取min,所以其他非0的初始化成最大值 for (int j = 1; j < dp.length; j++) { dp[j] = Integer.MAX_VALUE; }
- 遍历顺序
// 因为dp数组存的是最少个数,也就无关排列和组合 // 先遍历物品再遍历背包 或者 先遍历背包再遍历物品都可以 for (int i = 0; i < coins.length; i++) for (int j = coins[i]; j <= amount; j++)
- 模拟dp数组
public int coinChange(int[] coins, int amount) {
// 凑成总金额为j的最少硬币个数
int[] dp = new int[amount + 1];
// 凑成总金额为0的最少硬币个数肯定是0
dp[0] = 0;
// 递推公式是取min,所以其他非0的初始化成最大值
for (int j = 1; j < dp.length; j++) {
dp[j] = Integer.MAX_VALUE;
}
// 因为dp数组存的是最少个数,也就无关排列和组合
// 先遍历物品再遍历背包 或者 先遍历背包再遍历物品都可以
for (int i = 0; i < coins.length; i++) {
for (int j = coins[i]; j <= amount; j++) {
// 避免后面整数最大值还要+1
if (dp[j - coins[i]] != Integer.MAX_VALUE) {
// 不取当前硬币 或者 取当前硬币,看哪个方法个数最少
dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);
}
}
}
if (dp[amount] == Integer.MAX_VALUE) {
return -1;
}
return dp[amount];
}
279. 完全平方数
题目讲解:代码随想录
重点:
- 理解求数量问题的遍历顺序。
- 理解for循环里的i * i
思路:
- dp数组的含义
// 和为j的完全平方数的最少数量 int[] dp = new int[n + 1];
- 递推公式
// 不用当前完全平方数的数量 和 用当前完全平方数的数量 取最小 dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
- dp数组的初始化
// 和为0的完全平方数的最少数量为0 dp[0] = 0; // 递推公式取min, 所以非0初始化为最大值 for (int j = 1; j < dp.length; j++) { dp[j] = Integer.MAX_VALUE; }
- 遍历顺序
// 求的是最少数量: 先遍历物品再遍历背包 或者 先遍历背包再遍历物品 都可以 for (int i = 1; i * i <= n; i++) for (int j = i * i; j <= n; j++)
- 模拟dp数组
public int numSquares(int n) {
// 和为j的完全平方数的最少数量
int[] dp = new int[n + 1];
// 和为0的完全平方数的最少数量为0
dp[0] = 0;
// 递推公式取min, 所以非0初始化为最大值
for (int j = 1; j < dp.length; j++) {
dp[j] = Integer.MAX_VALUE;
}
// 求的是最少数量,先遍历物品再遍历背包 或者 先遍历背包再遍历物品 都可以
for (int i = 1; i * i <= n; i++) {
for (int j = i * i; j <= n; j++) {
// 不用当前完全平方数的数量 和 用当前完全平方数的数量 取最小
dp[j] = Math.min(dp[j], dp[j - i * i] + 1);
}
}
return dp[n];
}
139. 单词拆分
题目讲解:代码随想录
重点:
- 理解递推公式的if判断
- 理解dp数组的含义
思路:
- dp数组的含义
// 字符串长度为j能否拆分 boolean[] dp = new boolean[s.length() + 1];
- 递推公式
// 递推公式 // 1. 判断当前字符串长度能否容纳当前单词 // 2. 判断当前字符串长度减去当前单词长度(上一个子串)是否为true // 3. 判断当前字符串子串是否等于当前单词 if (j >= wordLen && dp[j - wordLen] && word.equals(s.substring(j - wordLen, j))) { dp[j] = true; // 当前子串匹配上了, 直接剪枝到下一个子串 break; }
- dp数组的初始化
// 长度为0, 直接不取任何字典里的单词 dp[0] = true;
- 遍历顺序
// 排列问题, 先遍历背包再遍历物品 for (int j = 1; j <= s.length(); j++) for (String word : wordDict)
- 模拟dp数组
public boolean wordBreak(String s, List<String> wordDict) {
// 字符串长度为j能否拆分
boolean[] dp = new boolean[s.length() + 1];
// 长度为0, 直接不取任何字典里的单词
dp[0] = true;
// 排列问题, 先遍历背包再遍历物品
for (int j = 1; j <= s.length(); j++) {
for (String word : wordDict) {
int wordLen = word.length();
// 递推公式
// 1. 判断当前字符串长度能否容纳当前单词
// 2. 判断当前字符串长度减去当前单词长度(上一个子串)是否为true
// 3. 判断当前字符串子串是否等于当前单词
if (j >= wordLen && dp[j - wordLen]
&& word.equals(s.substring(j - wordLen, j))) {
dp[j] = true;
// 当前子串匹配上了, 直接剪枝到下一个子串
break;
}
}
}
return dp[s.length()];
}
多重背包理论基础
理论讲解:代码随想录
重点:
- 多重背包和01背包是非常像的, 为什么和01背包像呢?每件物品最多有i件可用,把i件摊开,其实就是一个01背包问题了。
- 从代码里可以看出多重背包是01背包里面再加一个for循环遍历一个每种商品的数量。
import java.util.Scanner;
class multi_pack{
public static void main(String [] args) {
Scanner sc = new Scanner(System.in);
/**
* bagWeight:背包容量
* n:物品种类
*/
int bagWeight, n;
//获取用户输入数据,中间用空格隔开,回车键换行
bagWeight = sc.nextInt();
n = sc.nextInt();
int[] weight = new int[n];
int[] value = new int[n];
int[] nums = new int[n];
for (int i = 0; i < n; i++) weight[i] = sc.nextInt();
for (int i = 0; i < n; i++) value[i] = sc.nextInt();
for (int i = 0; i < n; i++) nums[i] = sc.nextInt();
// 多重背包问题代码如下
int[] dp = new int[bagWeight + 1];
//先遍历物品再遍历背包,作为01背包处理
for (int i = 0; i < n; i++) {
for (int j = bagWeight; j >= weight[i]; j--) {
// **重点:遍历每种物品的个数**
for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++) {
dp[j] = Math.max(dp[j], dp[j - k * weight[i]] + k * value[i]);
}
}
}
System.out.println(dp[bagWeight]);
}
}
背包问题总结
总结讲解:代码随想录
背包问题的分类及动规五部曲
- dp数组的含义
- 递推公式
- dp数组的初始化
- 确定遍历顺序
- 模拟dp数组
不同类型问题的递推公式总结
首先要理解dp数组的含义,理解了就知道递推公式怎么写。
- 问能否能装满背包 或者 最多装多少
dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);- 问装满背包有几种方法
dp[j] += dp[j - nums[i]];- 问背包装满最大价值
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);- 问装满背包所有物品的最小个数
dp[j] = Math.min(dp[j], dp[j - coins[i]]);遍历顺序
01背包
分两种情况: dp数组是二维和dp数组是一维:
- 二维数组: 先正序遍历物品再正序遍历背包 或者 先正序遍历背包再正序遍历物品 都可以
- 一维数组: 先正序遍历物品再倒序遍历背包
完全背包
一维数组分三种情况: 纯完全背包,求组合,求排列
- 纯完全背包: 先正序遍历物品再正序遍历背包 或者 先正序遍历背包再正序遍历物品 都可以
- 求组合: 先正序遍历物品再正序遍历背包
- 求排列: 先正序遍历背包再正序遍历物品
思维导图



530

被折叠的 条评论
为什么被折叠?



