动态规划之01背包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
-
确定dp数组以及下标的含义
对于背包问题,有⼀种写法, 是使⽤⼆维数组,即dp[i][j] 表示从下标为[0-i]的物品⾥任意取,放进容量为j的背包,价值总和最⼤是多少 -
确定递推公式
再回顾⼀下dp[i][j]的含义:从下标为[0-i]的物品⾥任意取,放进容量为j的背包,价值总和最⼤是多少。
那么可以有两个⽅向推出来dp[i][j],
由dp[i - 1][j]推出,即背包容量为j,⾥⾯不放物品i的最⼤价值,此时dp[i][j]就是dp[i - 1][j]
由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数组如何初始化
关于初始化,⼀定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱。
⾸先从dp[i][j]的定义出发,如果背包容量j为0的话,即dp[i][0],⽆论是选取哪些物品
package ywy;
/**
* @author 是狸猫啊!
* @version 1.0
*/
public class ywy {
public static void main(String[] args) {
int[] wight = {1, 3, 4};
int[] value = {15, 20, 30};
int bagWeight = 4;
int[][] dp = new int[wight.length + 1][bagWeight + 1];
//二维数组的初始化
//我自己在做这个题的时候,有点不太理解这里为什么要进行的是倒序遍历
//其实这有一个好处,就是可以dp[0][0]这个为1 其余的值是15 只对第一行进行初始化,
//为什么不对第一列进行初始化啊?
//那是因为j = 0 的时候是对第一列的元素进行的初始化,代表的是背包的容量为0的时候,后面会填充进去的.
for (int j = bagWeight; j >= wight[0]; j--) {
dp[0][j] = dp[0][j - wight[0]] + value[0];
}
for (int i = 0; i < dp.length; i++) {
for (int j = 0; j < dp[i].length; j++) {
System.out.print(dp[i][j]);
}
System.out.println();
}
//进行背包的填充
for (int i = 1; i < wight.length; i++) {
for (int j = 0; j <= bagWeight; j++) {
if (j < wight[i]) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = Math.max(dp[i - 1][j],dp[i -1][j - wight[i]] + value[i]);
}
}
}
System.out.println("======================================");
for (int[] ints :dp) {
for (int i :ints) {
System.out.print(i);
}
System.out.println();
}
}
}
一维dp数组(对背包进行一个压缩)
- 在⼀维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最⼤为dp[j]
- dp[j - weight[j]]我觉得这个东西更多是一个现在背包容量的问题,j表示的背包的容量,你把前面的那个数一减,加上现在背包的容量
重点解读:
为什么不能够进行对j不能够进行正序遍历了
- 如果正序遍历
dp[1] = dp[1 - weight[0]] + value[0] = 15
dp[2] = dp[2 - weight[0]] + value[0] = 30 此时dp[2]就已经是30了,意味着物品0,被放⼊了两次,所以不能正序遍历。 - 那么问题⼜来了,为什么⼆维dp数组历的时候不⽤倒叙呢?
因为对于⼆维dp,dp[i][j]都是通过上⼀层即dp[i - 1][j]计算⽽来,本层的dp[i][j]并不会被覆盖!
(如何这⾥读不懂,⼤家就要动⼿试⼀试了,空想还是不靠谱的,实践出真知!)
再来看看两个嵌套for循环的顺序,代码中是先遍历物品嵌套遍历背包容量,那可不可以先遍历背包容量
嵌套遍历物品呢?
public static void text(){
int[] wight = {1, 3, 4};
int[] value = {15, 20, 30};
int bagWeight = 4;
//进行dp数组的初始化
int[] dp = new int[bagWeight + 1];
for (int i = 0; i < wight.length; i++) {
for (int j = dp.length; j > 0; j++) {
dp[j] = dp[j - wight[i]] + value[i];
}
}
System.out.println(dp[dp[dp.length - 1]]);
}
] + value[i];
}
}
System.out.println(dp[dp[dp.length - 1]]);
}