1. 01背包问题 二维
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
对于背包问题,有一种写法, 是使用二维数组
即dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
当容量从1开始增大到 j
那么当容量为 j 时,对于一个物品,只有取和不取两种状态:
不取 i,那 就是在 i-1 中任取 重量为 j 的物品 最大值即为 dp[i - 1][j]
取i,那么最大值就要从前一个状态推到而来,即从 i-1 物品 中取 j-weight[i] 重量 的最大价值(因为要留weight[i] 的重量给 i 用),加上当前 i 的价值
// weight数组的大小 就是物品个数
for(int i = 1; i < weight.size(); i++) { // 遍历物品
for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
// weight数组的大小 就是物品个数
for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
for(int i = 1; i < weight.size(); i++) { // 遍历物品
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
2. 01背包问题 一维
倒序遍历背包:
因为在二维数组中,dp[i][j]是依据上一行的左边推导出来的,所以一维数组应该从右向左(倒序)遍历 (左边的数值没有被覆盖,才可以用)
//遍历顺序:先遍历物品,再遍历背包容量
for (int i = 0; i < wLen; i++){
for (int j = bagWeight; j >= weight[i]; j--){
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
}
}
3. 分割等和子集
可以看成是 重量为 sum/2 的背包 能不能装 价值为11的物品
物品即是数字 每个数字的值 就是 重量 价值
class Solution {
public boolean canPartition(int[] nums) {
int n = nums.length;
int sum = 0;
for(int num : nums) {
sum += num;
}
//奇数不能平分
if(sum % 2 != 0) return false;
int target = sum / 2; // 背包重量
int[] dp = new int[target + 1];
for(int i = 0; i < n; i++) { // 物品 即每一个正数
for(int j = target; j >= nums[i]; j--) {
//物品 i 的重量是 nums[i],其价值也是 nums[i]
dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
}
//剪枝
if(dp[target] == target)
return true;
}
return dp[target] == target;
}
}