- Backpack
中文English
Given n items with size Ai, an integer m denotes the size of a backpack. How full you can fill this backpack?
Example
Example 1:
Input: [3,4,8,5], backpack size=10
Output: 9
Example 2:
Input: [2,3,5,7], backpack size=12
Output: 12
Challenge
O(n x m) time and O(m) memory.
O(n x m) memory is also acceptable if you do not know how to optimize memory.
Notice
You can not divide any item into small pieces.
解法1:二维数组
代码如下:
class Solution {
public:
/**
* @param m: An integer m denotes the size of a backpack
* @param A: Given n items with size A[i]
* @return: The maximum size
*/
int backPack(int m, vector<int> &A) {
int n = A.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n; ++i) {
// for (int j = m; j >= A[i]; --j) {
// for (int j = A[i - 1]; j <= m; ++j) {
for (int j = 1; j <= m; ++j) { //也可以写成 for (int j = m; j >= 1; --j)
if (j >= A[i - 1]) {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - A[i - 1]] + A[i - 1]);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[n][m];
}
};
注意:
- 第二个for循环里面,j必须从1开始。写成
for (int j = 1; j <= m; ++j)
和
for (int j = m; j >= 1; --j)
都是可以的。
为什么写成for (int j = m; j >= 1; --j)也可以呢? 因为是两层循环,不管j是从大到小,还是从小到大,计算dp[i][j]的时候,dp[i - 1][j - A[i - 1]]肯定已经在i-1这上一轮循环里面计算过了。
但不能写成
for (int j = m; j >= A[i]; --j) {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - A[i - 1]] + A[i - 1]);
}
也不能写成
for (int j = A[i - 1]; j <= m; ++j) {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - A[i - 1]] + A[i - 1]);
}
因为dp[i][0 … A[i]]这段一直都是0。
2) for i和for j两个循环位置可以互换。
解法2:用一维数组优化
代码如下:
class Solution {
public:
/**
* @param m: An integer m denotes the size of a backpack
* @param A: Given n items with size A[i]
* @return: The maximum size
*/
int backPack(int m, vector<int> &A) {
int n = A.size();
vector<int> dp(m + 1, 0);
for (int i = 0; i < n; ++i) {
for (int k = m; k >= A[i]; --k) {
//for (int k = A[i]; k <= m; ++k) { //wrong!!!
dp[k] = max(dp[k], dp[k - A[i]] + A[i]);
}
}
return dp[m];
}
};
注意:
1)第二个for循环不能写成for (int k = A[i]; k <= m; ++k)。为什么呢?因为
里面的dp[k] = max(dp[k], dp[k - A[i]] + A[i])实际上相当于
dp[i][k] = max(dp[i - 1][k], dp[i - 1][k - A[i]] + A[i])
如果k是从大到小的话,那么dp[k]计算的时候dp[k - A[i]]确实是上一轮的dp[k - A[i]]。
但是如果k是从小到大的话,那么dp[k]计算的时候dp[k - A[i]]就是本轮的dp[k - A[i]]。那么结果就不对了。
2)上面的写法实际上等价于
int backPack(int m, vector<int> &A) {
int n = A.size();
vector<int> dp(m + 1, 0);
for (int i = 0; i < n; ++i) {
for (int k = m; k >= 0; --k) {
if (k >= A[i])
dp[k] = max(dp[k], dp[k - A[i]] + A[i]);
else
dp[k] = dp[k];
}
}
return dp[m];
}
但dp[k]=dp[k]纯属多余,第2个循环可以简化成
for (int k = m; k >= A[i]; --k) {
dp[k] = max(dp[k], dp[k - A[i]] + A[i]);