uacs2024

导航

leetcode 377. 组合总和 Ⅳ

377. 组合总和 Ⅳ

没写出来🤡

官方题解

class Solution {
public:
//当1 ≤ i ≤ target 时,如果存在一种排列,其中的元素之和等于 i,则该排列的最后一个元素一定是数组 nums 中的一个元素。
//假设该排列的最后一个元素是 num,则一定有 num ≤ i,对于元素之和等于 i−num 的每一种排列,在最后添加 num 之后即可得到一个元素之和等于 i 的排列,
//因此在计算 dp[i] 时,应该计算所有的 dp[i−num] 之和。
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target+1);//dp[i]代表 i 可由nums元素组合的个数
        dp[0] = 1;//只有当不选取任何元素时,元素之和才为 0,因此只有 1 种方案。
        for(int i = 1;i <= target;++i){
        //遍历 i 从 1 到 target,对于每个 i,进行如下操作:
            for(int &num : nums){
            //遍历数组 nums 中的每个元素 num,当 num≤i 时,将 dp[i−num] 的值加到 dp[i]。
                if(num <= i && dp[i-num] < INT_MAX - dp[i]){
                    dp[i] += dp[i-num];
                }
            }
        }
        return dp[target];
    }
};

如果给定的数组中含有负数,则会导致出现无限长度的排列。

例如,假设数组 nums 中含有正整数 a 和负整数 −b(其中 a>0,b>0,−b<0),则有 a×b+(−b)×a=0,对于任意一个元素之和等于 target 的排列,在该排列的后面添加 b 个 a 和 a 个 −b 之后,得到的新排列的元素之和仍然等于 target,而且还可以在新排列的后面继续 b 个 a 和 a 个 −b。因此只要存在元素之和等于 target 的排列,就能构造出无限长度的排列。

如果允许负数出现,则必须限制排列的最大长度,避免出现无限长度的排列,才能计算排列数。

关于INT_MAX的问题,出错的用例如下: [10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180,190,200,210,220,230,240,250,260,270,280,290,300,310,320,330,340,350,360,370,380,390,400,410,420,430,440,450,460,470,480,490,500,510,520,530,540,550,560,570,580,590,600,610,620,630,640,650,660,670,680,690,700,710,720,730,740,750,760,770,780,790,800,810,820,830,840,850,860,870,880,890,900,910,920,930,940,950,960,970,980,990,111] target=999

这个用例的答案只有一种,就是9个111 。可以看到前面n-1个数都是10的倍数,是不可能得到和为999的。 但是dp计算的过程中会计算和为0-999所有数字。例如计算dp[990], 排列数目会有很多,可能越界。

最符合理论的解法是:使用unsigned long long, 这样完全满足传递方程, 保证中间计算值不越界。

官方的解法有偷巧的地方,因为最终结果保证不超过INT_MAX, 所以超过就不传递了。实际确实也不传递,假设dp[990] > INT_MAX, dp[999] 实际无法通过dp[990]传递,因为nums里面没有一个值是9.

总结:dp中间值可能大于INT_MAX, 但是目标值是小于INT_MAX的。测试用例特殊,目标值实际不需要中间值(大于INT_MAX)的传递。

灵神题解

class Solution {
public:
    int dfs(vector<int>& nums,int target,vector<int> &memo){
        if(target == 0)  return 1;
        if(memo[target] != -1)  return memo[target];//之前算过
        memo[target] = 0;
        for(int &num : nums){
            if(num <= target){
                memo[target] += dfs(nums,target-num,memo);
            }
        }
        return memo[target];
    }
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> memo(target+1,-1);
        return dfs(nums,target,memo);
    }
};

 

posted on 2024-12-28 17:56  ᶜʸᵃⁿ  阅读(35)  评论(0)    收藏  举报