LeetCode每日一题: 377. 组合总和 Ⅳ

377. 组合总和 Ⅳ

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum-iv
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目

给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

题目数据保证答案符合 32 位整数范围。

示例 1:

输入:nums = [1,2,3], target = 4
输出:7
解释:
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。


提示:

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 1000
  • nums 中的所有元素 互不相同
  • 1 <= target <= 1000

思路及代码

我上来就是一个回溯!很快啊!超时了= =。

(其实只有5个测试用例没过了,面向测试用例编程一波倒也可以解决了……想想还是算了)

class Solution {
    int num = 0;

    public int combinationSum4(int[] nums, int target) {
        comSum(target,nums);
        return num;
    }

    public void comSum(int x, int[] nums) {
        if(x<=0) {
            num++;
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (x - nums[i] >= 0) {
                comSum(x - nums[i],nums);
            }
        }
    }
}

主要确实想不到动态规划的思路,虽然很像完全背包问题,但选择顺序还是有区别的,所以似乎还是递归+记忆化搜索更容易有思路一点,于是再把上面的回溯改成递归传值的形式后,再补充一下记忆化数组,就有了下面的代码:

(补充记忆化数组的过程还是比较难的,我没有什么思路导致卡了很久,也是瞄了一眼评论区才想到的吧,动态规划做多了递归+记忆化都不太熟悉了= =看来还有得练啊。其实做一下前置的题更好一点吧……)

class Solution {
    int[] m;

    public int combinationSum4(int[] nums, int target) {

        m = new int[target + 1];
        Arrays.fill(m, -1);
        m[0] = 1;

        return comSum(target, nums);
    }

    public int comSum(int x, int[] nums) {
        // 这是一点细节,如果没有填充-1,只是利用不等于0来作为递归出口的话,
        // 就会导致做一些无用的递归,也就是本来应该返回0的数据没有正确返回,依旧递归下去了
        // 那么后续运算会慢一些,最后一个测试用例过不去,会超时
        if (m[x] != -1) {
            return m[x];
        }
        int res = 0;
        for (int num : nums) {
            if (x >= num) {
                res += comSum(x - num, nums);
            }
            m[x] = res;
        }
        return res;
    }
}

最后基于这样的形式去转变成动态规划,就出来了最终的代码:

public int combinationSum4(int[] nums, int target) {
    int n = nums.length;
    int[] dp = new int[target + 1];
    dp[0] = 1;

    for (int i = 0; i <= target; i++) {
        for (int j = 0; j < n; j++) {
            if (i >= nums[j]) {
                dp[i] += dp[i - nums[j]];
            }
        }
    }
    return dp[target];
}

有能力的话还可以再结合题解重新从背包问题的角度思考动态规划的思路,我就不细说了。

另外回溯的时候还想到一种思路,递归方法再传个xi的下标,然后每次循环就从xi开始,再搞个集合记录一下数组元素和值,最后递归结束的时候就是找到了一种排列方式,直接计算出这些元素排列组合的个数(如:1112233的排列个数就是:c(7,3) * c(4,2) * c(2,2)),再让nums加上就行了。不过想了想做全排列还是有点麻烦的,倒也没必要去硬做数学题吧,主要全排列编码实现稍微还是有点麻烦,并且效率不见得好。大致代码如下:

int num = 0;
Map<Integer, Integer> map;

public int combinationSum4(int[] nums, int target) {
    	map = = new HashMap<>();
        comSum(target, 0, nums);
        return res;
}

public void comSum(int x, int xi, int[] nums) {
    if (x == 0) {
        num +=/*全排列的值,遍历map计算得到。*/;
        map = new HashMap<>();
        return;
    }
    int res = 0;

    for (int i = xi; i < nums.length; i++) {
        if (x - nums[i] >= 0) {
            map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
            comSum(x - nums[i], i, nums);
        }
    }
    return res;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值