LintCode 564: Combination Sum IV (DP 经典题,难)

这题类似完全背包。
代码如下:

class Solution {
public:
    /**
     * @param nums: an integer array and all positive numbers, no duplicates
     * @param target: An integer
     * @return: An integer
     */
    int backPackVI(vector<int> &nums, int target) {
        int n = nums.size();
        vector<int> dp(target + 1, 0);  //dp[i] is the number of possible combinations that add up to i
        
        dp[0] = 1;
        
        for (int j = 0; j <= target; ++j) {
            for (int i = 0; i < n; ++i) {
                if (j >= nums[i])
                    dp[j] += dp[j - nums[i]];    
  //              cout<<"i="<<i<<" j="<<j<<" dp="<<dp[j]<<endl;
            }
        }
        return dp[target];
    }
};

注意:
1)循环i和循环j不能互换(即不能先i循环,再j循环)。否则出错。
以nums=[1,2,4]和target=4为例,
上面的打印信息为:
i=0 j=0 dp=1
i=1 j=0 dp=1
i=2 j=0 dp=1
i=0 j=1 dp=1
i=1 j=1 dp=1
i=2 j=1 dp=1
i=0 j=2 dp=1
i=1 j=2 dp=2
i=2 j=2 dp=2
i=0 j=3 dp=2
i=1 j=3 dp=3
i=2 j=3 dp=3
i=0 j=4 dp=3
i=1 j=4 dp=5
i=2 j=4 dp=6

但如果我们把循环i和循环j的顺序倒过来,即

        for (int i = 0; i < n; ++i) {
            for (int j = nums[i]; j <= target; ++j) {
                dp[j] += dp[j - nums[i]];    
                //cout<<"i = "<<i<<" j="<<j<<" dp="<<dp[j]<<endl;
            }
        }

则打印信息为:
i=0 j=1 dp=1
i=0 j=2 dp=1
i=0 j=3 dp=1
i=0 j=4 dp=1
i=1 j=2 dp=2
i=1 j=3 dp=2
i=1 j=4 dp=3
i=2 j=4 dp=4

为什么呢?因为顺序很重要!
该题的6种组合是:
[1, 1, 1, 1]
[1, 1, 2]
[1, 2, 1]
[2, 1, 1]
[2, 2]
[4]
return 6
这里 [1, 1, 2], [1, 2, 1] and [2, 1, 1] 算了3组。而通常的01背包问题里面,这3组加起来只能算一组,因为是一个一个取的。

所以这里的2重循环必须反过来,列优先(相比之下通常的01背包问题是行优先)。这样,对于每个j,i都要走一遍,就能够保证[1,1,2],[1,2,1]和[2,1,1]都被考虑到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值