【动态规划】【01背包问题】
- 解法 二维dp数组01背包
- 解法 一维dp数组(滚动数组)01背包
---------------🎈🎈题目链接🎈🎈-------------------
解法 二维dp数组01背包
😒: 我的代码实现============>
动规五部曲
✒️确定dp数组以及下标的含义
dp[i][j] 表示从下标为[0-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]);
✒️dp数组初始化
空间为0时,所有物品都放不进去,价值都为0
物品0的重量超过书包重量时,放不进去,价值为0。超过后就可以放入,此时价值为物品0的价值,value[0]
✒️确定遍历顺序
先遍历背包和先遍历物品都可以
✒️举例推导dp数组
时间复杂度O(N)
空间复杂度O(N)
📘代码
import java.util.*;public class Main{public static void main (String[] args) {Scanner scanner = new Scanner(System.in);int goodscount = scanner.nextInt(); //物品个数int bagsize = scanner.nextInt(); //书包大小int[] space = new int[goodscount];int[] value = new int[goodscount];scanner.nextLine();for(int i = 0; i < goodscount ; i++){space[i] = scanner.nextInt();}scanner.nextLine();for(int j = 0; j < goodscount ; j++){value[j] = scanner.nextInt();}// dp[i][j]:代表0-i号物品 放到j大小的背包中 的最大价值int[][] dp = new int[goodscount][bagsize+1];//初始化dp数组 // 背包重量为0的时候,所有物品对应的价值均为0for(int i = 0; i < goodscount; i++){dp[i][0] = 0;}//物品0:只有在其重量小于等于背包容量 bagsize 时,对应的dp[i][j]才等于其价值value[0],否则就都是0for(int i = space[0]; i <= bagsize; i++){dp[0][i] = value[0];}// 递推表达式:dp[i][j] = max( dp[i-1][j] , dp[i-1][j-weight[i]] + value[i] )for(int i = 1; i < goodscount; i++){for(int j = 1; j <= bagsize; j++){if(j<space[i]){// 当当前书包容量小于物品i重量space[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-space[i]] + value[i] );}}}System.out.println(dp[goodscount-1][bagsize]);}
}
解法 一维dp数组(滚动数组)01背包
😒: 我的代码实现============>
动规五部曲
✒️确定dp数组以及下标的含义
dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。。
✒️确定递推公式
不放物品i:dp[j]
放物品i:dp[j - weight[i]] + value[i]
所以递归公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
✒️dp数组初始化
空间为0时,所有物品都放不进去,值都为0:dp[0] = 0
其他下初始化:dp数组在推导的时候一定是取价值最大的数,如果题目给的价值都是正整数那么非0下标都初始化为0就可以了。
✒️确定遍历顺序
先正向遍历物品
再反向遍历背包容量
✒️举例推导dp数组
📘代码
package ACM;import java.util.*;public class test{public static void main (String[] args) {int[] weight = {1, 3, 4};int[] value = {15, 20, 30};int bagWeight = 4;testWeightBagProblem(weight, value, bagWeight);}public static void testWeightBagProblem(int[] weight, int[] value, int bagWeight){// 【dp[j]:代表背包容量为j时候 能获得的最大价值】int[] dp = new int[bagWeight+1];//【初始化dp数组】// 背包重量为0的时候,价值为0,其余初始化均为0dp[0] = 0;// 二维递推表达式:dp[i][j] = max( dp[i-1][j] , dp[i-1][j-weight[i]] + value[i] )// 一维滚动数组:dp[j] = max( dp[j], dp[j-weight[i]]+value[i] )// 【遍历顺序:正序遍历物品i + 倒序遍历背包容量j】// 用物品0遍历背包 0 15 15 15 15// 物品0,背包容量为bagWight=4时,能获得的最大价值 dp[4] = max(不放物品0:0, 放物品0:dp[4-weight[0]] + value[0]) = 15// 物品0,背包容量为3时,能获得的最大价值 15 dp[3] = max(0, dp[3-weight[0]] + value[0]) = 15// 物品0,背包容量为2时,能获得的最大价值 15 dp[2] = max(0, dp[2-weight[0]] + value[0]) = 15// 物品0,背包容量为1时,能获得的最大价值 15 dp[1] = max(0, dp[1-weight[0]] + value[0]) = 15// 物品0,背包容量为0时,能获得的最大价值 0 0// 用物品1遍历背包 0 15 15 20 35// 物品1,背包容量为bagWight=4时,能获得的最大价值 dp[4] = max(dp[4], dp[4-weight[1]] + value[1]) = max(15, 15+20) = 35// 物品1,背包容量为3时,能获得的最大价值 15 dp[3] = max(dp[3], dp[3-weight[1]] + value[1]) = max(15, 0+20) = 20// 物品1,背包容量为2时,能获得的最大价值 15 dp[2] = max(dp[2], 物品1放不下:0) = max(15, 0) = 15// 物品1,背包容量为1时,能获得的最大价值 15 dp[1] = max(dp[1], 物品1放不下:0) = max(15, 0) = 15// 物品1,背包容量为0时,能获得的最大价值 0 0// 用物品2遍历背包// ...// 物品weight.length-1for(int i = 0; i < weight.length; i++){ // 正向遍历物品ifor(int j = bagWeight; j >= weight[i]; j--){ // 反向遍历背包容量jdp[j] = Math.max(dp[j] , dp[j-weight[i]]+value[i]);}}//打印dp数组for (int j = 0; j <= bagWeight; j++){System.out.print(dp[j] + " ");}}
}