动态规划 part04
day42-1 ● 1049. 最后一块石头的重量 II
解决思路
把石头尽可能分成两堆,如果两堆的重量如果相似的话,那么相撞后所剩的值就是最小值
类似:分割等和子集
1背包问题:每个物品只能用一次
dp含义
dp[j]
表示容量(这里说容量更形象,其实就是重量)为 j
的背包,最多可以背最大重量为 dp[j]
。
本题中,石头的重量是 stones[i],石头的价值也是 stones[i]
递推公式
weight[i] == value[i]
即:
初始化
dp[0] == 0 其他也初始化为 0
遍历顺序
遍历物品:顺序遍历
遍历背包:逆序遍历
代码实现
class Solution {
// 一维数组
public int lastStoneWeightII(int[] stones) {
int sum = 0;
for(int i : stones){
sum +=i;
}
int target = sum >> 1;
// 初始化dp数组
int[] dp = new int[target + 1];
for(int i = 0; i < stones.length; i++){
// 采用倒序
for(int j = target; j >= stones[i]; j--){
dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
}
}
return sum - 2 * dp[target];
}
}
class Solution {
//
public int lastStoneWeightII(int[] stones) {
int sum = 0;
for(int i : stones){
sum +=i;
}
int target = sum >> 1;
// 初始化, dp[i][j]为可以放0-i物品,背包容量为j的情况下背包中的最大价值
int[][] dp = new int[stones.length][target + 1];
// dp[i][0]默认初始化为0
// dp[0][j]取决于stones[0]
for(int j = stones[0]; j < target; j++){
dp[0][j] = stones[0];
}
for(int i = 1; i < stones.length; i++){
for(int j = 1; j <= target; j++){
if(j >= stones[i]){
// 不放 dp[i - 1][j] 放:dp[i - 1][j - stones[i]] + stones[i]
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - stones[i]] + stones[i]);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
// System.out.println(dp[stones.length - 1][target]);
return (sum - dp[stones.length - 1][target]) -dp[stones.length - 1][target];
}
}
打印/总结
和 416. 分割等和子集 很像
day42-2 ●494. 目标和
解决思路
和回溯算法中的组合总和很像
dp含义
dp[j]
装满为 j
有 dp[j]
种方法
递推公式
dp[j] += dp[j - num[i]]
初始化
以前:
dp[0] = 0 装满价值为0的背包所背的价值为0
本题:
dp[0] = 1 装满价值为0的背包有1种方法
遍历顺序
每个物品只能用一次所以是01背包
遍历物品:顺序
遍历背包:逆序
代码实现
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = 0;
for(int i =0; i<nums.length;i++){
sum+=nums[i];
}
// 如果target的绝对值大于sum,那么没有方案
if(Math.abs(target) > sum) return 0;
// 如果target+sum除以2的余数不为0,也没有方案
if((target + sum) % 2 == 1) return 0;
int bagSize = (target + sum) / 2;
int[] dp = new int[bagSize + 1];
// 初始化
dp[0] = 1;
for(int i = 0; i < nums.length; i++){ // 物品
for(int j = bagSize; j >= nums[i]; j--){ // 背包 倒叙遍历为了让物品只能使用一次
dp[j] += dp[j - nums[i]];
}
}
return dp[bagSize];
}
}
打印/总结
本题:装满背包有多少种方法
分割等和子集: 能不能装满背包
最后一块石头:尽量装这个背包
纯01背包问题:装满这个背包它的最大价值是多少
初始化:不要纠结字面意思,可以带入一个场景去看一下 eg.[0] target=0 dp[0]=1
重点理解 递推公式:dp[j] += dp[j - nums[i]],这个公式后面的提问
day42-3 ●474.一和零
解决思路
多重背包:
每个物品里面限制一下物品的个数
本题和之前的01背包的题目的区别:
纯01背包解决的是给定容器,问装满这个容器的最大价值是多少
分割等和子集:给定容器,问能不能装满
最后一块石头的重量:给定容量的背包,尽量装,最多能装的重量是多少
目标和:给定容器,问装满这个背包有多少种方法
本题:装满这个背包最多有多少个物品
dp含义
dp[i][j]
i
个0
,j
个 1
最大背 dp[i][j]
个物品
递推公式
初始化
避免初始值把求的值覆盖掉
dp[i][j] = 0
遍历顺序
遍历物品:顺序遍历
遍历背包:倒叙遍历
代码实现
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
// dp[i][j] 表示i个0和j个1时的最大子集
int[][] dp = new int[m + 1][n + 1];
int zeroNum, oneNum;
for(String str : strs){ // 顺序遍历物品
zeroNum = 0;
oneNum = 0;
for(char ch : str.toCharArray()){
if(ch == '0'){
zeroNum++;
}else{
oneNum++;
}
}
// 倒序遍历背包
for(int i = m; i >= zeroNum; i--){
for(int j = n; j >= oneNum; j--){
dp[i][j] = Math.max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
}
}
}
return dp[m][n];
}
}
打印/总结
粗略了解, 01背包,完全背包,多重背包的区别,不过不用细扣,因为后面 对于 完全背包,多重背包 还有单独题目
多重背包:
每个物品里面限制一下物品的个数
本题和之前的01背包的题目的区别:
纯01背包解决的是给定容器,问装满这个容器的最大价值是多少
分割等和子集:给定容器,问能不能装满
最后一块石头的重量:给定容量的背包,尽量装,最多能装的重量是多少
目标和:给定容器,问装满这个背包有多少种方法
本题:装满这个背包最多有多少个物品