01背包理论基础
1、01背包
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
2、示例
背包最大重量 4,物品重量[1, 3, 4],物品价值[15, 20, 30]。问背包能背的物品最大价值是多少?
3、二维dp数组01背包
1、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得到的最大价值
2、所以递推公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
3、dp数组初始化:当背包的容量为0,即j=0时,dp[i][0] = 0; 其他情况 i 的情况都由 i-1 推导得出,所以 i = 0的情况需要初始化。
当只有物品0时,当背包容量大于等于物品0重量时,背包的价值为物品0的价值,初始化之后为:
![01背包.png](https://img-blog.csdnimg.cn/img_convert/7ae6693fd3ceb28453a3b08a272a173a.png)
对于其他的值 初始化为0 即可,后续会覆盖掉,所以最初声明dp数组时全部填充0方便些。
4、遍历顺序:由于dp[i][j]的值依赖于其左上角的值,所以遍历顺序为 从左到右,从上到下。对于先遍历物品或背包重量,虽然两个for循环遍历的次序不同,但是dp[i][j]所需要的数据就是左上角,根本不影响dp[i][j]公式的推导!
5、打印dp数组
public class TwoDP {
public static void main(String[] args) {
int[] weight = {1, 3, 4};
int[] value = {15, 20, 30};
int bagSize = 4;
int n = solution(weight, value, bagSize);
System.out.println(n);
}
private static int solution(int[] weight, int[] value, int bagSize) {
int goods = weight.length;
int[][] dp = new int[goods][bagSize + 1]; // 创建dp数组
for (int[] ints : dp) {
Arrays.fill(ints, 0);
}
// 初始化dp数组
for (int j = weight[0]; j < bagSize + 1; j++) {
dp[0][j] = value[0];
}
// 遍历dp数组
for (int i = 1; i < goods; i++) {
for (int j = 1; j < bagSize + 1; j++) {
if (j < weight[i]) {
/**
* 当前背包的容量都没有当前物品i大的时候,是不放物品i的
* 那么前i-1个物品能放下的最大价值就是当前情况的最大价值
*/
dp[i][j] = dp[i - 1][j];
} else {
int a = dp[i - 1][j];// 不放物品 i 的最大价值
int b = dp[i - 1][j - weight[i]] + value[i]; // 放入物品 i 的最大价值
dp[i][j] = Math.max(a, b);
}
}
}
for (int[] ints : dp) {
System.out.println(Arrays.toString(ints));
}
return dp[goods - 1][bagSize];
}
}
01背包理论基础(一维滚动数组)
1、dp数组含义
dp[j] 表示容量为j的背包,所容纳的物品的最大价值为dp[j]。
2、递推公式
dp[j] 的推导,1:不放新的物品,则最大价值不变为dp[j];2:放入物品 i,dp[j] = dp[j - weight[i]] + value[i],即容量为j-weight[i]的背包的最大价值加上当前物品的价值;然后取1 和 2 的最大值。
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
3、初始化
当j=0,即背包容量为0时,dp[j]=0; 其他值 也初始化为0;
4、遍历顺序
一维dp数组的遍历顺序如下
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
遍历背包容量时需要倒序,倒序遍历是为了保证物品i只被放入一次!
public class TwoDP {
public static void main(String[] args) {
int[] weight = {1, 3, 4};
int[] value = {15, 20, 30};
int bagSize = 4;
int m = solutionTwo(weight, value, bagSize);
System.out.println("m-> " + m);
}
private static int solutionTwo(int[] weight, int[] value, int bagSize) {
int goods = weight.length;
int[] dp = new int[bagSize + 1];//dp[j] 表示容量为j的背包,所容纳的物品的最大价值为dp[j]。
Arrays.fill(dp, 0); //初始化
for (int i = 0; i < goods; i++) {
for (int j = bagSize; j >= weight[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
}
}
return dp[bagSize];
}
}