LeetCode第 377 题:组合总数 IV(C++)

377. 组合总和 Ⅳ - 力扣(LeetCode)
在这里插入图片描述

在这里插入图片描述

先看这个图,就明白很多了,相当于计算叶子节点数量。
其实这题更容易想到dfs回溯,但是重复子问题太多的话,可能会超时,所以最好使用dp。

参考:希望用一种规律搞定背包问题 - 组合总和 Ⅳ - 力扣(LeetCode)

顺序相关,实际在求排列数

这是一个完全背包问题:

物品(nums的元素)选取不限制,物品就是数组元素,背包容量就是target,求的是刚好装满背包的方法数。完全背包问题的大体代码模板就是:

int dp[target+1];//dp[i]表示和值为 i 的时候的最大价值。
...
for()//枚举物品
	for()//枚举容量
		dp[j] = max(dp[j], dp[j-v]+w);

这题求的是排列数,所以把max处改为相加。

int dp[target+1];//dp[i]表示和值为 i 的时候可能的组合数。
...
for()//枚举物品
	for()//枚举容量
		dp[j] += dp[j-v];

但是这样其实还不行,这样实际上是在求组合数(顺序无关),而本题实际是在求排列数(顺序相关)。完全背包一维dp的时候,两次for循环实际上是可以颠倒的。但是在这儿,必须进行颠倒,也就是必须先枚举容量,再枚举物品,原因是我们需要保证每次更新dp数组值的时候,所有的物品都是可选的,这样才能保证求的是排列数。

初始值dp[0] = 1, 也就是和值为0的选取方法只有一种(那就是不选),需要的是dp[target]:

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        if(nums.size() == 0)    return 0;
        vector<unsigned long long> dp(target+1, 0);//dp[i]表示和值为 i 的时候可能的组合数。
        dp[0] = 1;
        for(int i = 0; i <= target; ++i){//因为实际上求的是排列数
            for(const auto &num : nums){//所以物品需要放在内循环,保证每次循环所有的物品都是可选的
                if(num <= i)    dp[i] += dp[i - num];
            }
        }
        return dp[target];
    }
};

进阶:

参考:动态规划 - 组合总和 Ⅳ - 力扣(LeetCode)

如果给定的数组中含有负数会怎么样?

如果有负数,相当于给定数组中的元素有了更多的组合,特别是出现了一对相反数的时候,例如题目中的示例 [-4, 1, 2, 3, 4],target = 4 的时候,-4 和 4 可以无限次地、成对添加到题目中的示例中,成为新的组合,那么这道问题就没有什么意义了。

问题会产生什么变化?
我们需要在题目中添加什么限制来允许负数的出现?

如果有负数参与进来,不能够与已有的正数的组合之和为 0 (分组背包,互斥);
或者限制负数的使用次数,设计成多重背包问题的样子。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值