LintCode 562: Backpack IV (完全背包问题变种,DP经典)

  1. Backpack IV
    中文English
    Given an integer array nums[] which contains n unique positive numbers, num[i] indicate the size of ith item. An integer target denotes the size of backpack. Find the number of ways to fill the backpack.

Each item may be chosen unlimited number of times

Example
Example1

Input: nums = [2,3,6,7] and target = 7
Output: 2
Explanation:
Solution sets are:
[7]
[2, 2, 3]
Example2

Input: nums = [2,3,4,5] and target = 7
Output: 3
Explanation:
Solution sets are:
[2, 5]
[3, 4]
[2, 2, 3]

解法1:
这题就是完全背包的变种。
dp[x]表示能
装满size 为x的背包的最多方法总数。
注意:

  1. dp[j] += dp[j - nums[i]];
    旧的dp[j]: 上次的# of ways to fill size=j knapsack with item i
    旧的dp[j-nums[i]]: 上次的# of ways to fill size=j-nums[i] knapsack without item i
    所以两者加起来就是这次的# of ways to fill size=j knapsack with item i。
  2. dp[0] = 1,要不然dp[]会全0。
  3. 此题为完全背包,所以第2层循环顺序为从小到大。
    附网上评论:
    无重复背包即01背包,得到的结果dp[i]是根据之前的结果来的,换句话说,是否选入当前物品是根据之前没有当前物品的子结果而做出的选择,是为了保证每一个物品的唯一性。
    有重复背包即完全背包,每个物品都有无限的可重复性,所以当前的结果要从之前已经出现过当前物品的子结果中得到。
    所以循环的方向要反一下,一个从下到上,一个从上到下。
class Solution {
public:
    /**
     * @param nums: an integer array and all positive numbers, no duplicates
     * @param target: An integer
     * @return: An integer
     */
    int backPackIV(vector<int> &nums, int target) {
        int n = nums.size();
        vector<int> dp(target + 1 , 0); //dp[x] shows the max ways that can fill size x knapsack
        
        dp[0] = 1;        
        for (int i = 0; i < n; ++i) {
            
            for (int j = nums[i]; j <= target; ++j) {

                dp[j] += dp[j - nums[i]];  //old dp[j] is the # of ways to fill j that do have item i,
                                           //dp[j - nums[i]] is the # of ways to fill j- nums[i] that do not have item i

            }
        }
        
        return dp[target];
    }
};

注意:

  1. 下面的方法不对:
class Solution {
public:
    /**
     * @param nums: an integer array and all positive numbers, no duplicates
     * @param target: An integer
     * @return: An integer
     */
    int backPackIV(vector<int> &nums, int target) {
        int n = nums.size();
        int count = 0;
        vector<int> dp(target + 1 , 0); //dp[x] shows the max volumes that the size x knapsack can hold. 
        
        for (int i = 0; i < n; ++i) {
            for (int j = nums[i]; j <= target; ++j) {
                dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
   //             cout<<"i = "<<i<<" j = "<<j<<" "<<dp[j]<<endl;
                if (dp[j] == target) count++;
            }
        }
        
        return count;
    }
};

该方法用dp[x]表示size=x的knapsack的最多可以装满的大小。
在j循环内判断dp[x]是否等于target,若是,则count++。最后返回count。
该方法的问题在什么地方呢?
我们针对输入
[2,2,3]
7
其debug信息如下:
i = 0 j = 2 2
i = 0 j = 3 2
i = 0 j = 4 4
i = 0 j = 5 4
i = 0 j = 6 6
i = 0 j = 7 6
i = 1 j = 2 2
i = 1 j = 3 2
i = 1 j = 4 4
i = 1 j = 5 4
i = 1 j = 6 6
i = 1 j = 7 6
i = 2 j = 3 3
i = 2 j = 4 4
i = 2 j = 5 5
i = 2 j = 6 6
i = 2 j = 7 7
Output
1
Expected
3

我们可以看出,在i,j两层循环种,dp只有1次达到了7。这是因为dp[7]实际上几种最优解都包括了,但只算了一种。
因为上面的dp[x]代表的表示size=x的knapsack的最多可以装满的大小,不代表有多少种方案能装满x,所以很多种解也只会算一种。
2) 这题也不能用LintCode 564: Combination SumIV的方法,即将i,j循环简单的倒过来。因为LintCode 564是取硬币,[1, 1, 2], [1, 2, 1] and [2, 1, 1] 算了3组,而这里只能算1组。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值