LintCode 92: Backpack (经典背包DP题)

  1. 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];
    }
};

注意:

  1. 第二个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]);
发布了577 篇原创文章 · 获赞 21 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/roufoo/article/details/102996040
92