leetcode377. 组合总和 Ⅳ(递归和dp解法(已更新dp解法以及dp思路))

该博客主要介绍了LeetCode 377题的解决方案,包括递归和动态规划两种方法。递归解法因重复计算导致超时,通过记忆化递归优化。重点在于动态规划解法,讲解了一维DP数组的转移方程,并解释了为何初始化为`target+1`长度,以及为何不能交换双重循环顺序的原因。
摘要由CSDN通过智能技术生成

想看动态规划详细思路直接往下面翻,在最下面

题目
给你一个由 不同 整数组成的数组 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:递归

不出所料,超时;
在这里插入图片描述

class Solution {
    public int combinationSum4(int[] nums, int target) {
        if(target == 0) {
            return 1;
        }
        if(target < 0) {
            return 0;
        }
        int ans = 0;
        for(int num:nums) {
            ans +=combinationSum4(nums, target-num);
        }
        return ans;
    }
}

上面的递归方法之所以会超时,是因为有重复计算,比如计算 dp(4) 的时候计算了 dp(2),而计算 dp(3) 的时候会再次计算 dp(2),这样大量的重复计算导致了超时的产生。如果在递归的过程中,把已经计算了的结果放在数组中保存,那么下次需要再次计算相同的值的时候,直接从数组中读取同样的计算结果,就能省下大量重复的计算。

那么,接下来采用记忆化递归的方式;

解法二(优化DFS):

(代码在图下面)
在这里插入图片描述

class Solution{
    public int combinationSum4(int[] nums, int target) {
        int arr[] = new int[target + 1];
        Arrays.fill(arr, -1);
        arr[0] = 1;
        return search(nums, target,arr);
    }

    private int search(int[] nums, int target,int arr[]) {
        if (arr[target] != -1) {
            return arr[target];
        }
        int ans = 0;
        for (int num : nums) {
            if (target >= num) {
                ans += search(nums, target - num,arr);
            }
        }
        arr[target] = ans;
        return ans;
    }
}

没什么好讲的,和第一种递归思路类似;有不懂的地方请在评论区留言,作者会回复

3.重点: dp解法(代码和思路在下面):

在这里插入图片描述
代码非常简练,但是思路值得推敲
这里new的dp数组中dp[ i ]的含义,是代表有i种组合方案的时候使用nums中的元素能组成的组成数的数量
也就是说可以理解为下面的公式,也就是一维情况的转移方程
dp[i]=dp[i-nums[0]]+dp[i-nums[1]]+dp[i-nums[2]]+…+dp[i-nums[target]]
这一步是一种dp技巧,需要多加练习

举例:
比如给定了nums = [1,3,5],target = 9;
相当于得到了dp[9] = dp[8] + dp[6] + dp[4];
意思是提出三种情况,分别是:
dp[9] = dp[8]+1;//降模后dp[8]+1种方案,下面同理
dp[9] = dp[6]+3;
dp[9] = dp[4]+5;
至于为什么dp[] = new int[target+1]而不是int[target];
是因为要考虑一种算上自己的情况:(举例如下)
dp[1] = dp[0]+0=1;

关于两层for循环交换的问题:
在我的做法中我将dp数组压缩成一维的数组,两次for循环不能交换位置;
如果交换位置,仅会出现小数在前的情况,例如只有1,1,2而缺少1,2,1这样的方案
但是如果展开为二维数组,那么交换两层for循环的顺序是可行的;可以自己试一下。

下面是代码:

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int dp[] = new int[target+1];
        dp[0]=1;
        for(int i=1;i <= target;i++)
        {
            for(int num:nums)
            {
                if(i >= num)
                {
                    dp[i] += dp[i-num];
                }
            }
        }
        return dp[target];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赵奕升

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值