01背包问题
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
动态规划:
创建一个二维数组dp[i][j],其中i表示物品的序号,j表示当前背包的容量,dp[i][j]表示使用0-i下标的物品,背包容量为j的最大价值总和。此时,dp[i][j]其实跟上一行dp[i - 1][j]的区别就在于是否使用物品i,故dp[i][j]的值相当于dp[i - 1][j]和dp[i][j - weight[i]] + value[i]的最大值。遍历顺序可以随意,可以先遍历物品也可以先遍历背包容量,只要保证两个元素都是从小到大即可。因为每个最大价值和在数组上都是由左上区域推出来的,所以只要保证两个都是从小到大就行。
总结:
dp定义及含义:dp[i][j]表示使用0-i下标的物品,背包容量为j的最大价值和。
状态转移方程:dp[i][j] = Math.max(dp[i - 1][j],dp[i][j - weight[i]] + value[i])。
初始化:第一列都是0,因为背包容量为0,放不下东西,第一列则根据第一个物品赋值
遍历顺序:物品和重量保证从小到大即可,两者之间的顺序无所谓。
dp[n][w]即为所要求的值。
优化: 使用一维数组
根据状态方程:dp[i][j] = Math.max(dp[i - 1][j],dp[i][j - weight[i]] + value[i]),在遍历赋值的每一行中,其实就是使用了前一行的数值dp[i - 1][j],i表示物品,可以进行优化。直接省略物品,在外层循环里判断j是否大于物品重量即可。即创建一个一维数组dp[j],状态转移方程改为:dp[j] = Math.max(dp[j],dp[j - weight[i]] + value[i])。遍历顺序:先遍历物品,背包容量倒序遍历。因为一维数组dp不能保证物品的唯一使用性,而dp的赋值需要前一个容量的背包配合。正序遍历的话,背包大的会重复存入背包小的物品。
总结:
dp定义及含义:dp[j]表示背包容量为j的最大价值和
状态转移方程:dp[j] = Math.max(dp[j],dp[j - weight[i]] + value[i])
初始化:dp[0] = 0 因为放不下物品
遍历顺序:先遍历物品,再倒序遍历背包容量
dp[w]即为所要求的值。
①、分割等和子集
给你一个 只包含正整数 的 非空 数组
nums
。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
事例:
输入:nums = [1,5,11,5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11] 。
思路:
将该题转化为01背包问题,此时nums表示物品,nums[i]既是物品重量也是物品价值,求等和子集,也就是求总和一半的子集。转化为背包问题,也就是求物品是否能填饱容量为总和一半的背包。
步骤:
将nums排序,求得总和一半target。创建数组dp[target + 1],初始化背包,在每行初始化遍历完后判断dp[target]是否等于target,若等于,则返回true,若不等,则返回false。因为背包问题中的状态转移方程:dp[j] = Math.max(dp[j],dp[j - weight[i]] + value[i]),可能造成溢出,故每行物品遍历中判断dp[target]。
动态规划:
dp定义及含义:
dp[j]表示背包容量为j的最大价值和
状态转移方程:dp[j] = Math.max(dp[j],dp[j - weight[i]] + value[i])
初始化:dp[0] = 0 因为放不下物品
遍历顺序:先遍历nums,再倒序遍历背包容量
判断dp[target] == target
代码:
public boolean canPartition(int[] nums) {
Arrays.sort(nums);
int target = 0;
for(int x : nums){
target += x;
}
if(target % 2 != 0) return false;
target = target / 2;
//二维数组
// int[][] dp = new int[nums.length][target + 1];
// for(int i = 0;i < nums.length;i++){
// dp[i][0] = 0;
// }
// for(int j = 0;j <= target;j++){
// if(nums[0] <= j) dp[0][j] = nums[0];
// }
// for(int i = 1;i < dp.length;i++){
// for(int j = 1;j < dp[0].length;j++){
// if(nums[i] <= j){
// dp[i][j] = Math.max(dp[i - 1][j - nums[i]] + nums[i],dp[i - 1][j]);
// }else{
// dp[i][j] = dp[i - 1][j];
// }
// }
// if(dp[i][dp[0].length - 1] == target) return true;
// }
//一维数组
int[] dp = new int[target + 1];
dp[0] = 0;
for(int i = 0;i < nums.length;i++){
for(int j = target;j > 0;j--){
if(j >= nums[i]){
//可以存放
dp[j] = Math.max(dp[j],dp[j - nums[i]] + nums[i]);
}
}
if(dp[target] == target) return true;
}
return false;
}