题目分析
- 假设dp(i,j)是最大承重为j、有前i件物品可选时的最大总价值,i∈[1,n],j∈[1,W]
- dp(i,0)、dp(0,j)初始值均为0
- 如果j< weights[i-1],那么dp(i,j)=
dp(i-1,j)
- 如果j≥ weights[i-1],那么dp(i,j)=max{
dp(i-1,j)
,dp(i-1,j- weights[i-1])
+values[i-1]
}
dp(3,7)就是当 capacity为7,取前3件物品的最大总价值
j≥最后一件物品时是有选择权的,当j<最后一件物品的重量,那么一定不能选
Solution - 二维数组
int maxValue1(int[] values, int[] weights, int capacity) {
if (values == null || values.length == 0) return 0;
if (weights == null || weights.length == 0) return 0;
if (values.length != weights.length || capacity <= 0) return 0;
int[][] dp = new int[values.length + 1][capacity + 1];
for (int i = 1; i <= values.length; i++) {
for (int j = 1; j <= capacity; j++) {
if (j < weights[i - 1]) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = Math.max(
dp[i - 1][j],
values[i - 1] + dp[i - 1][j - weights[i - 1]]);
}
}
}
return dp[values.length][capacity];
}
Solution - 一维数组
-
dp(i,j)都是由dp(i-1,k)推导出来的,也就是说,第i行的数据是由它的上一行第i-1行推导出来的
因此,可以使用一维数组来优化 -
当出现最大承重=物品重量,是最后一次有选择, 当小于物品重量,必然是不能选的,就是上一行的
int maxValue(int[] values, int[] weights, int capacity) {
if (values == null || values.length == 0) return 0;
if (weights == null || weights.length == 0) return 0;
if (values.length != weights.length || capacity <= 0) return 0;
int[] dp = new int[capacity + 1];
for (int i = 1; i <= values.length; i++) {
for (int j = capacity; j >= weights[i - 1]; j--) {
dp[j] = Math.max(dp[j], values[i - 1] + dp[j - weights[i - 1]]);
}
}
return dp[capacity];
}
变式题 - 恰好装满
■有n件物品和一个最大承重为W的背包,每件物品的重量是w、价值是v
口在保证总重量恰好等于W的前提下,选择某些物品装入背包,背包的最大总价值是多少?
口注意:每个物品只有1件,也就是每个物品只能选择0件或者1件
■dp(i,j)初始状态调整
口dp(i,0)=0,总重量恰好为0,最大总价值必然也为0
口dp(0,j)=-∞(负无穷),j≥1,负数在这里代表无法恰好装满
对于dp(1,9),如果选了(6,2)最大价值就是6+上一行7位置,如果不是负无穷,如能会出现正数,但是只要是凑不够就是不合理的
Solution
int maxValueExactly(int[] values, int[] weights, int capacity) {
if (values == null || values.length == 0) return 0;
if (weights == null || weights.length == 0) return 0;
if (values.length != weights.length || capacity <= 0) return 0;
int[] dp = new int[capacity + 1];
for (int j = 1; j <= capacity; j++) {
dp[j] = Integer.MIN_VALUE;
}
for (int i = 1; i <= values.length; i++) {
for (int j = capacity; j >= weights[i - 1]; j--) {
dp[j] = Math.max(dp[j], values[i - 1] + dp[j - weights[i - 1]]);
}
}
return dp[capacity] < 0 ? -1 : dp[capacity];
}
Reference:小码哥MJ