在
n
个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为m
,每个物品的大小为A[i]
。
样例:
样例 1:
输入: [3,4,8,5], backpack size=10
输出: 9
样例 2:
输入: [2,3,5,7], backpack size=12
输出: 12
挑战:
O ( n × m ) O(n \times m) O(n×m) time and O ( m ) O(m) O(m) memory.
O ( n × m O(n \times m O(n×m) memory is also acceptable if you do not know how to optimize memory.
注意事项
你不可以将物品进行切割。
分析:最原始的背包问题。每个物品有「选」与「不选」两种选择。
注意:初始化的时候,只要是容量大于等于 A[0]
的容量,都应该把下标为 0
的这件物品选进去。
Java 代码:
public class Solution {
/**
* @param m: An integer m denotes the size of a backpack
* @param A: Given n items with size A[i]
* @return: The maximum size
*/
public int backPack(int m, int[] A) {
int len = A.length;
int[][] dp = new int[len][m + 1];
// 初始化要小心
for (int j = 0; j <= m ; j++) {
if (A[0] <= j) {
dp[0][j] = A[0];
}
}
for (int i = 1; i < len; i++) {
for (int j = 0; j <= m; j++) {
// 不选 A[i]
dp[i][j] = dp[i - 1][j];
if (A[i] <= j) {
// 选 A[i]
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - A[i]] + A[i]);
}
}
}
return dp[len - 1][m];
}
public static void main(String[] args) {
Solution solution = new Solution();
// int m = 10;
// int[] A = {3, 4, 8, 5};
// int m = 12;
// int[] A = {2, 3, 5, 7};
int m = 90;
int[] A = {12, 3, 7, 4, 5, 13, 2, 8, 4, 7, 6, 5, 7};
int res = solution.backPack(m, A);
System.out.println(res);
}
}
暂时不优化空间。先保证代码好懂。
技巧 1:使用「哨兵」,避免单独初始化
Java 代码:
public class Solution {
/**
* @param m: An integer m denotes the size of a backpack
* @param A: Given n items with size A[i]
* @return: The maximum size
*/
public int backPack(int m, int[] A) {
int len = A.length;
// 多设置的这一行是「哨兵」
// 可以认为是第 0 件物品的体积为 0
// 这样就可以免去初始化的过程
int[][] dp = new int[len + 1][m + 1];
// 后面所有 dp 的第 1 个维度都要加 1
for (int i = 0; i < len; i++) {
for (int j = 0; j <= m; j++) {
// 不选 A[i]
dp[i + 1][j] = dp[i][j];
if (A[i] <= j) {
// 选 A[i]
dp[i + 1][j] = Math.max(dp[i][j], dp[i][j - A[i]] + A[i]);
}
}
}
// 注意这里应该返回 dp[len][m]
return dp[len][m];
}
}
技巧 2:状态压缩
- 由于只关心表格最后一行、最后一个状态的值;
- 填写每一行只参考了它上一行的正上方,以及上一行左边的数值。
所以,可以只使用一行,倒过来填写。
想不到没有关系,这种知识点。只需要别人告诉我们,我们理解以后懂得运用即可。
Java 代码:
public class Solution {
/**
* @param m: An integer m denotes the size of a backpack
* @param A: Given n items with size A[i]
* @return: The maximum size
*/
public int backPack(int m, int[] A) {
// 状态压缩的代码,依然是加上了哨兵
int len = A.length;
int[] dp = new int[m + 1];
for (int i = 0; i < len; i++) {
// 注意:这样写,就省去了把上一行的值抄下来的过程,请读者体会
// 注意这里要改成 j--
for (int j = m; j >= 0; j--) {
if (A[i] <= j) {
dp[j] = Math.max(dp[j], dp[j - A[i]] + A[i]);
}
}
}
return dp[m];
}
}