北京网站建设哪家设计好,网站开发交接表,公司注销的详细流程,中山百度网站推广目录 背包问题01背包二维dp数组01背包一维 dp 数组#xff08;滚动数组#xff09;分割等和子集 LeetCode
背包问题 01背包
有n件物品和一个最多能背重量为 w 的背包#xff0c;第i件物品的重量是weight[i]#xff0c;得到的价值是value[i] 。每件物品只能用一次#x… 目录 背包问题01背包二维dp数组01背包一维 dp 数组滚动数组分割等和子集 LeetCode
背包问题 01背包
有n件物品和一个最多能背重量为 w 的背包第i件物品的重量是weight[i]得到的价值是value[i] 。每件物品只能用一次求解将哪些物品装入背包里物品价值总和最大。
暴力的解法 回溯时间复杂度就是 o ( 2 n ) o(2^n) o(2n)这里的n表示物品数量。
暴力的解法是指数级别的时间复杂度。进而才需要动态规划的解法来进行优化。
举例 背包最大重量为4。
重量价值物品0115物品1320物品2430
问背包能背的物品最大价值是多少
二维dp数组01背包 dp数组dp[i][j] 表示从下标为[0-i]的物品里任意取放进容量为j的背包价值总和最大是多少。 递推公式两个方向推出来dp[i][j] 不放物品 i 由dp[i - 1][j]推出即背包容量为j里面不放物品i的最大价值此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时物品i无法放进背包中所以背包内的价值依然和前面相同。)放物品i由dp[i - 1][j - weight[i]]推出dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值那么dp[i - 1][j - weight[i]] value[i] 物品i的价值就是背包放物品i得到的最大价值 所以递推公式 dp[i][j] max(dp[i - 1][j], dp[i - 1][j - weight[i]] value[i]); 初始化 如果背包容量 j 为 0 的话dp[i][0] 无论选取哪些物品背包价值总和一定为0。 状态转移方程 dp[i][j] max(dp[i - 1][j], dp[i - 1][j - weight[i]] value[i]); 可以看出i 是由 i-1 推导出来那么i为0的时候就一定要初始化。 dp[0][j]即i为0存放编号0的物品的时候各个容量的背包所能存放的最大价值。 当 j weight[0]的时候dp[0][j] 应该是 0因为背包容量比编号0的物品重量还小。 当j weight[0]时dp[0][j] 应该是value[0]因为背包容量放足够放编号0物品。
for (int j 0 ; j weight[0]; j) { // 当然这一步如果把dp数组预先初始化为0了这一步就可以省略但很多同学应该没有想清楚这一点。dp[0][j] 0;
}
// 正序遍历
for (int j weight[0]; j bagweight; j) {dp[0][j] value[0];
}遍历顺序 两个遍历维度 物品与背包重量 先遍历物品再遍历背包的过程如图所示 先遍历背包再遍历物品呢如图
public class BagProblem {public static void main(String[] args) {int[] weight {1,3,4};int[] value {15,20,30};int bagSize 4;testWeightBagProblem(weight,value,bagSize);}/*** 动态规划获得结果* param weight 物品的重量* param value 物品的价值* param bagSize 背包的容量*/public static void testWeightBagProblem(int[] weight, int[] value, int bagSize){// 创建dp数组int goods weight.length; // 获取物品的数量int[][] dp new int[goods][bagSize 1];// 初始化dp数组// 创建数组后其中默认的值就是0for (int j weight[0]; j bagSize; j) {dp[0][j] value[0];}// 填充dp数组for (int i 1; i weight.length; i) {for (int j 1; j bagSize; j) {if (j weight[i]) {/*** 当前背包的容量都没有当前物品i大的时候是不放物品i的* 那么前i-1个物品能放下的最大价值就是当前情况的最大价值*/dp[i][j] dp[i-1][j];} else {/*** 当前背包的容量可以放下物品i* 那么此时分两种情况* 1、不放物品i* 2、放物品i* 比较这两种情况下哪种背包中物品的最大价值最大*/dp[i][j] Math.max(dp[i-1][j] , dp[i-1][j-weight[i]] value[i]);}}}// 打印dp数组for (int i 0; i goods; i) {for (int j 0; j bagSize; j) {System.out.print(dp[i][j] \t);}System.out.println(\n);}}
}
一维 dp 数组滚动数组
在使用二维数组的时候递推公式dp[i][j] max(dp[i - 1][j], dp[i - 1][j - weight[i]] value[i]);
其实可以发现如果把dp[i - 1]那一层拷贝到dp[i]上表达式完全可以是dp[i][j] max(dp[i][j], dp[i][j - weight[i]] value[i]);
与其把dp[i - 1]这一层拷贝到dp[i]上不如只用一个一维数组了只用dp[j]一维数组也可以理解是一个滚动数组。
这就是滚动数组的由来需要满足的条件是上一层可以重复利用直接拷贝到当前层。 dp[j]表示容量为j的背包所背的物品价值可以最大为dp[j]。
dp[j] max(dp[j], dp[j - weight[i]] value[i]);
dp数组初始化的时候都初始为0就可以了。
注意 遍历背包的顺序是倒序遍历保证物品只放入一次。
从后往前循环每次取得状态不会和之前取得状态重合这样每种物品就只取一次了。
public static void main(String[] args) {int[] weight {1, 3, 4};int[] value {15, 20, 30};int bagWight 4;testWeightBagProblem(weight, value, bagWight);
}public static void testWeightBagProblem(int[] weight, int[] value, int bagWeight){int wLen weight.length;//定义dp数组dp[j]表示背包容量为j时能获得的最大价值int[] dp new int[bagWeight 1];//遍历顺序先遍历物品再遍历背包容量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]);}}//打印dp数组for (int j 0; j bagWeight; j){System.out.print(dp[j] );}
分割等和子集
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集使得两个子集的元素和相等。
本题要求集合里能否出现总和为 sum / 2 的子集。
背包的体积为sum / 2背包要放入的商品集合里的元素重量为 元素的数值价值也为元素的数值背包如果正好装满说明找到了总和为 sum / 2 的子集。背包中每一个元素是不可重复放入。
dp[j]表示 背包总容量所能装的总重量是j放进物品后背的最大重量为dp[j]。
dp[j] max(dp[j], dp[j - nums[i]] nums[i]);
dp[0]一定是0。如果题目给的价值都是正整数那么非0下标都初始化为0就可以了如果题目给的价值有负数那么非0下标就要初始化为负无穷。这样才能让dp数组在递推的过程中取得最大的价值而不是被初始值覆盖了。
如果使用一维dp数组物品遍历的for循环放在外层遍历背包的for循环放在内层且内层for循环倒序遍历
class Solution {public boolean canPartition(int[] nums) {if(nums null || nums.length 0) return false;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--) {dp[j] Math.max(dp[j], dp[j - nums[i]] nums[i]);}if(dp[target] target) return true;} return dp[target] target;}
}