leetcode.494. 目标和---DFS+DP(背包问题)

该博客介绍了如何利用深度优先搜索(DFS)和动态规划(DP)方法解决寻找数组中不同表达式以达到特定目标和的问题。在DFS中,通过递归地尝试所有可能的符号组合来计数目标和的表达式。而在DP方法中,通过建立二维数组来存储部分和,简化问题并避免重复计算,从而找到满足条件的组合数。博客还提供了两种方法的详细代码实现作为示例。
摘要由CSDN通过智能技术生成

494. 目标和

给你一个整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :

例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

示例 1:

输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3
示例 2:

输入:nums = [1], target = 1
输出:1
 

提示:

1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 100

题解:

方法一:DFS+回溯

  • 我们将数组放进深搜方法中,对两种方向加或减深搜即可。注意的是由于第一个参与计算的数也是要分带符号和不带符号的,所以我们最好在此数前专门定义一个0,这样的话就可以当成正常的计算了。

代码:

class Solution {
    int count = 0;
    public int findTargetSumWays(int[] nums, int target) {
        dfs(nums,target,0,0);
        return count;
    }

    public void dfs(int[] nums,int target,int index,int sum){
        if(index==nums.length){
            if(sum==target){
                count++;
            }
            return;
        }
        
        dfs(nums,target,index+1,sum+nums[index]);
        dfs(nums,target,index+1,sum-nums[index]);

    }
}

方法二:动态规划

  • 我们设加负号的元素和为sumf,加加号的元素和为sumz,sum为元素和。那么有:

     	 sumf + sumz = sum;
    
  • 有target,可得关于target的表达式:

     	 sumz - sumf = target;
    
  • 二者合并后,可以剔除一个变量,因此有:

     	 sumf = (sum - target)/2;
    

需要注意的是此时是有条件的,即sumf不能为负数,且不能为除不尽的情况,若有则直接返回0。

  • 而sumf由于为应该加负号的元素和,因此这里即相当于我们把问题转变为了从数组中挑选若干个应该加负号元素,且他们的元素和为sumf。可见这里即转化为了经典的二维dp----背包问题。即设dp[i][j]表示在数组nums的前 i 个数中选取元素,使得这些元素之和等于 j 的方案数

根据每次遍历得到的nums中的元素,我们不难得到状态转移方程:
在这里插入图片描述
而由于关系式中出现了i-1,因此对于i=0的所有项,我们都要先确定值,即此时为确定边界。且注意的是dp[0][0] 是= 0的!

代码:

class Solution {

    public int findTargetSumWays(int[] nums, int target) {
        int l = nums.length;
        int sum = 0;
        for(int i=0;i<nums.length;i++){
            sum+=nums[i];
        }
        if(sum-target<0 || (sum-target)%2!=0){
            return 0;
        }
        int sumf = (sum-target)/2;
        int[][] dp = new int[l+1][sumf+1];
        dp[0][0] = 1;

        for(int j=1;j<=sumf;j++){
            dp[0][j] = 0;
        }

        for(int i=1;i<=nums.length;i++){
            for(int j=0;j<=sumf;j++){
                if(nums[i-1] > j){//此时j才为真正的容量!
                    dp[i][j] = dp[i-1][j];
                }
                else{
                    dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i-1]];
           //后面这个不用加一,因为他只是确定了一个元素的情况,并不是确定了一种表达式的情况
                }
            }
        }

        return dp[l][sumf];
    }

   
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

向光.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值