LintCode 125: Backpack II (0-1背包问题, DP经典)

125 · Backpack II
Algorithms
Medium

Description
There are n items and a backpack with size m. Given array A representing the size of each item and array V representing the value of each item.

What’s the maximum value can you put into the backpack?

A[i], V[i], n, m are all integers.
You can not split an item.
The sum size of the items you want to put into backpack can not exceed m.
Each item can only be picked up once
m <= 1000m<=1000
len(A),len(V)<=100len(A),len(V)<=100

Example
Example 1:

Input:

m = 10
A = [2, 3, 5, 7]
V = [1, 5, 2, 4]
Output:

9
Explanation:

Put A[1] and A[3] into backpack, getting the maximum value V[1] + V[3] = 9

Example 2:

Input:

m = 10
A = [2, 3, 8]
V = [2, 5, 8]
Output:

10
Explanation:

Put A[0] and A[2] into backpack, getting the maximum value V[0] + V[2] = 10

Challenge
O(nm) memory is acceptable, can you do it in O(m) memory?

Tags
Related Problems
1538
Card Game II
Medium
800
Backpack IX
Medium
724
Minimum Partition
Medium
700
Cutting a Rod
Medium
669
Coin Change
Medium
588
Partition Equal Subset Sum
Medium
564
Combination Sum IV
Medium
563
Backpack V
Medium
562
Backpack IV
Medium
440
Backpack III
Medium
92
Backpack
Medium

本文参考了九章的背包教程:

解法1:
经典DP解法。
时间复杂度O(MN),空间复杂度O(MN)。
注意:dp[m+1][n+1],如果定义成dp[m][n],dp[0][]就有歧义:到底表示不取任何包,还是取包0呢?

class Solution {
public:
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @param V: Given n items with value V[i]
     * @return: The maximum value
     */
    int backPackII(int m, vector<int> &A, vector<int> &V) {
        int itemCount = A.size();
        //dp[itemCount][packSize]
        vector<vector<int>> dp(itemCount + 1, vector<int>(m + 1, 0));
        
        for (int i = 1; i <= itemCount; ++i) {
            for (int j =1; j <= m; ++j) {
                dp[i][j] = dp[i - 1][j];
                if (j >= A[i - 1]) {
                    dp[i][j] = max(dp[i][j], dp[i - 1][j - A[i - 1]] + V[i - 1]);
                }
                    
            }
        }        
        return dp[itemCount][m];
    }
};

解法2:
在解法1的基础上加上滚动数组优化
时间复杂度O(MN)
空间复杂度O(N)

class Solution {
public:
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @param V: Given n items with value V[i]
     * @return: The maximum value
     */
    int backPackII(int m, vector<int> &A, vector<int> &V) {
        int itemCount = A.size();
        //dp[2][packSize]
        vector<vector<int>> dp(2, vector<int>(m + 1, 0));
        int rotate = 0;
        
        for (int i = 0; i <= itemCount; ++i) {
            rotate ^= 1;
            for (int j = 0; j <= m; ++j) {
                dp[rotate][j] = dp[rotate ^ 1][j];
                if (j >= A[i - 1]) {
                    dp[rotate][j] = max(dp[rotate][j], dp[rotate ^ 1][j - A[i - 1]] + V[i - 1]);
                }
            }
        }
        
        return dp[rotate][m];
    }
};

解法3:
将第2层循环倒序,因为i时候的dp[j - A[i - 1]]实际上就是i-1时候的dp[j - A[i - 1]],因为倒序的时候,dp[j]更新了,此时dp[j - A[i - 1]]还未更新。
时间复杂度还是O(MN)。
空间复杂度还是O(N)。但只用了一个一维数组。

class Solution {
public:
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @param V: Given n items with value V[i]
     * @return: The maximum value
     */
    int backPackII(int m, vector<int> &A, vector<int> &V) {
        int itemCount = A.size();
        //dp[packSize]
        vector<int> dp(m + 1, 0);
        
        for (int i = 1; i <= itemCount; ++i) {
            for (int j = m; j >= A[i - 1]; --j) {
                dp[j] = max(dp[j], dp[j - A[i - 1]] + V[i - 1]);
            }
        }
        
        return dp[m];
    }
};

注意:
1)Solution 1,2,3的i-1都可以写成i的形式。即:

    int backPackII(int m, vector<int> &A, vector<int> &V) {
        int itemCount = A.size();
        //dp[packSize]
        vector<int> dp(m + 1, 0);
        
        for (int i = 0; i < itemCount; ++i) {
            for (int j = m; j >= A[i]; --j) {
                dp[j] = max(dp[j], dp[j - A[i]] + V[i]);
            }
        }
        
        return dp[m];
    }
  1. 一个很多人想不通的问题:DP算法时间复杂度O(nW), n是item个数,W是包大小。为什么还说01背包是NP问题呢?

九章某牛的解答如下:

题目做到的背包容量和物品大小基本上都为整数,因为只有是整数才能用DP算法解决。但对于实际的应用场景,整数是必然不够用的,那么就必须将物品大小和背包容量按其最小的精度划分DP单元,例如我的背包容量为36.857692,其他的物品大小也相应地精确到了六位小数,此时为了求解这个问题,我必须对背包容量的每一个0.000001分配一个DP单元,从0.000000,0.000001,0.000002一直分配到36.857692,这时背包的实际大小只有36.857692,但是需要计算的单元却达到了36857693个之多。而对于只需要精确到个位的背包问题而言,即使我背包容量很大,比如说50000,那也只需要开50000大小的数组即可。所以抛开有效数字谈空间大小是无意义的。

所以事实上我们真正想要(只要能快速求解就能产生价值)的是能恣意地增加有效数字的位数(真正的问题规模),算法运行的时间还能在可控范围内(多项式时间内),但很可惜没办法做到,因为每增加一位有效数字,复杂度都变为原先的10倍,所以实际上的时间复杂度为O(物品数目*10^有效数字的位数),仍然是指数级别的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值