1049.最后一块石头的重量II
题目讲解:代码随想录
重点:
- 如何把题目转换成01背包问题
- 理解背包容量的设定及如何返回
思路:
- dp数组的含义
// 背包容量为j能装的最大重量 int[] dp = new int[target + 1];
- 递推公式
// 取放石头i或者不放石头i的最大值 dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
- dp数组的初始化
// 全部初始化为0,避免后续取最大的更新被初始值覆盖 dp[j] = 0
- 遍历顺序
// 先正序遍历石头,再倒叙遍历背包容量 for (int i = 0; i < stones.length; i++) for (int j = target; j >= stones[i]; j--)
- 模拟dp数组
public int lastStoneWeightII(int[] stones) {
// 求石头重量和及背包目标容量
int sum = 0;
for (int stone : stones) {
sum += stone;
}
int target = sum >> 1;
// 背包容量为j能装的最大重量
int[] dp = new int[target + 1];
// 先正序遍历石头,再倒叙遍历背包容量
for (int i = 0; i < stones.length; i++) {
for (int j = target; j >= stones[i]; j--) {
// 取放石头i或者不放石头i的最大值
dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
}
}
// 返回特别需要注意
// 因为target是除2向下取整,(sum - dp[target])肯定是大的那一堆石头
return (sum - dp[target]) - dp[target];
}
494. 目标和
题目讲解:代码随想录
重点:
- 加法的总和为x,那么减法的总和就是(sum-x)。所以x - (sum-x) = target,x = (target + sum) / 2。此时问题就转化为,用nums装满容量为x的背包,有几种方法。注意是有几种方法。
- 背包问题求有几种组合方法的递推公式通常都有+=而不是取max
- 理解递推公式,不是求max而是加在一起了。
- 理解dp数组的含义。
二维数组解法
思路:
- dp数组的含义
// dp[i][j]为采用[0,i]的数字装满背包容量为j的背包有多少种方法 int[][] dp = new int[nums.length][x + 1];
- 递推公式
// 当前背包容量放不下数字i if (nums[i] > j) { dp[i][j] = dp[i - 1][j]; } else { // 当前背包容量放得下数字i // 能够装满背包容量为j的背包分两种情况,一种放数字i,一种不放数字i dp[i][j] = dp[i - 1][j] + dp[i-1][j - nums[i]]; }
- dp数组的初始化
// 初始化第一行背包容量刚好为nums[0]能够装满有1种方法 if (x >= nums[0]) { dp[0][nums[0]] = 1; } // 初始化第一列背包容量为0,可以放数值为0的数字 int numZero = 0; for (int i = 0; i < nums.length; i++) { if (nums[i] == 0) { numZero++; } dp[i][0] = (int)Math.pow(2, numZero); }
- 遍历顺序
// 先正序遍历物品,再正序遍历背包 for (int i = 1; i < nums.length; i++) for (int j = 1; j <= x; j++)
- 模拟dp数组
public int findTargetSumWays(int[] nums, int target) { // 计算nums的和及检查是否存在方法 int sum = 0; for (int num : nums) { sum += num; } if (sum < Math.abs(target)) { return 0; } if ((sum + target) % 2 != 0) { return 0; } // 计算背包的最大容量 int x = (sum + target) / 2; // dp[i][j]为用[0,i]的数字装满背包容量为j的背包有多少种方法 int[][] dp = new int[nums.length][x + 1]; // 初始化第一行背包容量刚好为nums[0]能够装满有1种方法 if (x >= nums[0]) { dp[0][nums[0]] = 1; } // 初始化第一列背包容量为0,可以放数值为0的数字 int numZero = 0; for (int i = 0; i < nums.length; i++) { if (nums[i] == 0) { numZero++; } dp[i][0] = (int)Math.pow(2, numZero); } // 先正序遍历物品,再正序遍历背包 for (int i = 1; i < nums.length; i++) { for (int j = 1; j <= x; j++) { // 当前背包容量放不下数字i if (nums[i] > j) { dp[i][j] = dp[i - 1][j]; } else { // 当前背包容量放得下数字i // 能够装满背包容量为j的背包分两种情况,一种放数字i,一种不放数字i dp[i][j] = dp[i - 1][j] + dp[i-1][j - nums[i]]; } } } // 返回采用所有数字并且装满背包容量为x有多少种方法 return dp[nums.length - 1][x]; }一维数组解法
思路:
- dp数组的含义
// 装满背包容量为j的背包有多少种方法 int[] dp = new int[x + 1];
- 递推公式
// 不放数字i能装满的方法数 + 放数字i能装满的方法数 dp[j] += dp[j - nums[i]];
- dp数组的初始化
// 背包容量为0的时候有1种方法 dp[0] = 1;
- 遍历顺序
// 先正序遍历数字,再倒序遍历背包容量 for (int i = 0; i < nums.length; i++) for (int j = x; j >= nums[i]; j--)
- 模拟dp数组
public int findTargetSumWays(int[] nums, int target) { // 计算nums的和及检查是否存在方法 int sum = 0; for (int num : nums) sum += num; if (sum < Math.abs(target)) return 0; if ((sum + target) % 2 != 0) return 0; // 计算背包的最大容量 int x = (sum + target) / 2; // 装满背包容量为j的背包有多少种方法 int[] dp = new int[x + 1]; // 背包容量为0的时候有1种方法 dp[0] = 1; // 先正序遍历数字,再倒序遍历背包容量 for (int i = 0; i < nums.length; i++) { for (int j = x; j >= nums[i]; j--) { // 不放数字i能装满的方法数 + 放数字i能装满的方法数 dp[j] += dp[j - nums[i]]; } } // 返回背包容量为x的方法数 return dp[x]; }
474. 一和零
题目讲解:代码随想录
重点:
- 理解strs就是物品,m和n是两个不同的背包。
- 理解dp数组的含义,递推公式,遍历顺序。
思路:
- dp数组的含义
// 最多有i个0和j个1的最大子集大小,i为0号背包的容量,j为1号背包的容量 int[][] dp = new int[m + 1][n + 1];
- 递推公式
// 不取当前str,取当前str // 取当前str就需要减去当前str的0和1的个数 dp[i][j] = Math.max(dp[i][j], dp[i - zeroCount][j - oneCount] + 1);
- dp数组的初始化
// 保证不会后续更新不会被初始值覆盖即可 dp[i][j] = 0;
- 遍历顺序
// 先正序遍历strs,再倒序遍历两个背包,两个背包的先后顺序无所谓 for (int i = m; i >= zeroCount; i--) for (int j = n; j >= oneCount; j--)
- 模拟dp数组
public int findMaxForm(String[] strs, int m, int n) {
// dp[i][j]: 最多有i个0和j个1的最大子集大小,i为0号背包的容量,j为1号背包的容量
int[][] dp = new int[m + 1][n + 1];
int zeroCount, oneCount;
// 先遍历物品strs
for (String str : strs) {
zeroCount = 0;
oneCount = 0;
// 计算当前str的0和1的个数
for (char ch : str.toCharArray()) {
if (ch == '0') {
zeroCount++;
} else {
oneCount++;
}
}
// 再倒序遍历两个背包
for (int i = m; i >= zeroCount; i--) {
for (int j = n; j >= oneCount; j--) {
// 不取当前str,取当前str
dp[i][j] = Math.max(dp[i][j], dp[i - zeroCount][j - oneCount] + 1);
}
}
}
return dp[m][n];
}

640

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



