Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LeetCode] 377. Combination Sum IV #377

Open
grandyang opened this issue May 30, 2019 · 2 comments
Open

[LeetCode] 377. Combination Sum IV #377

grandyang opened this issue May 30, 2019 · 2 comments

Comments

@grandyang
Copy link
Owner

grandyang commented May 30, 2019

 

Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target.

Example:

_**nums**_ = [1, 2, 3]
_**target**_ = 4

The possible combination ways are:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)

Note that different sequences are counted as different combinations.

Therefore the output is _**7**_.

Follow up:
What if negative numbers are allowed in the given array?
How does it change the problem?
What limitation we need to add to the question to allow negative numbers?

Credits:
Special thanks to @pbrother for adding this problem and creating all test cases.

 

这道题是组合之和系列的第四道,博主开始想当然的以为还是用递归来解,结果写出来发现 TLE 了,的确 OJ 给了一个 test case 为 [4,1,2] 32,这个结果是 39882198,用递归需要好几秒的运算时间,实在是不高效,估计这也是为啥只让返回一个总和,而不是返回所有情况,不然机子就爆了。而这道题的真正解法应该是用 DP 来做,解题思想有点像之前爬梯子的那道题 Climbing Stairs,这里需要一个一维数组 dp,其中 dp[i] 表示目标数为i的解的个数,然后从1遍历到 target,对于每一个数i,遍历 nums 数组,如果 i>=x, dp[i] += dp[i - x]。这个也很好理解,比如说对于 [1,2,3] 4,这个例子,当计算 dp[3] 的时候,3可以拆分为 1+x,而x即为 dp[2],3也可以拆分为 2+x,此时x为 dp[1],3同样可以拆为 3+x,此时x为 dp[0],把所有的情况加起来就是组成3的所有情况了,参见代码如下:

 

解法一:

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target + 1);
        dp[0] = 1;
        for (int i = 1; i <= target; ++i) {
            for (auto a : nums) {
                if (i >= a) dp[i] += dp[i - a];
            }
        }
        return dp.back();
    }
};

 

如果 target 远大于 nums 数组的个数的话,上面的算法可以做适当的优化,先给 nums 数组排个序,然后从1遍历到 target,对于i小于数组中的数字x时,直接 break 掉,因为后面的数更大,其余地方不变,参见代码如下:

 

解法二:

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target + 1);
        dp[0] = 1;
        sort(nums.begin(), nums.end());
        for (int i = 1; i <= target; ++i) {
            for (auto a : nums) {
                if (i < a) break;
                dp[i] += dp[i - a];
            }
        }
        return dp.back();
    }
};

 

我们也可以使用递归+记忆数组的形式,不过这里的记忆数组用的是一个 HashMap。在递归函数中,首先判断若 target 小于0,直接返回0,若 target 等于0,则返回1。若当前 target 已经在 memo 中存在了,直接返回 memo 中的值。然后遍历 nums 中的所有数字,对每个数字都调用递归,不过此时的 target 要换成 target-nums[i],然后将返回值累加到结果 res 中即可,参见代码如下:

 

解法三:

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        unordered_map<int, int> memo;
        return helper(nums, target, memo);
    }
    int helper(vector<int>& nums, int target, unordered_map<int, int>& memo) {
        if (target < 0) return 0;
        if (target == 0) return 1;
        if (memo.count(target)) return memo[target];
        int res = 0, n = nums.size();
        for (int i = 0; i < n; ++i) {
            res += helper(nums, target - nums[i], memo);
        }
        return memo[target] = res;
    }
};

 

Github 同步地址:

#377

 

类似题目:

Combination Sum

Combination Sum II

Combination Sum III

 

参考资料:

https://leetcode.com/problems/combination-sum-iv/

https://leetcode.com/problems/combination-sum-iv/discuss/85079/My-3ms-Java-DP-solution

https://leetcode.com/problems/combination-sum-iv/discuss/85036/1ms-Java-DP-Solution-with-Detailed-Explanation

https://leetcode.com/problems/combination-sum-iv/discuss/85120/C%2B%2B-template-for-ALL-Combination-Problem-Set

 

LeetCode All in One 题目讲解汇总(持续更新中...)

@ericuni
Copy link

ericuni commented Oct 2, 2019

请问 dp[0] = 1 要怎么理解? 虽然从结果来倒推回去, 需要dp[0] 为1, 但是含义还是无法想清楚

@grandyang
Copy link
Owner Author

请问 dp[0] = 1 要怎么理解? 虽然从结果来倒推回去, 需要dp[0] 为1, 但是含义还是无法想清楚

因为所有的 dp 值都是根据前面的 dp 值累加出来的,必须要有个起始值,不然都是0的话,加来加去也一直会是0。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants