力扣 494. 目标和(中等)

题目

给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
在这里插入图片描述
提示:
1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 100

题解

1、回溯
数组nums的每个元素都可以添加符号+或-,因此每个元素都有两种选择,n个元素就有 2 n 2^n 2n种选择。提示里数组元素个数不超过20,因此可以使用回溯法,把每个元素两种选择的结果都加起来。所有表达式到结尾处,判断其值是否等于target,是的话就返回1,表示得到该表达式符合,否则返回0,表示该表达式不符合。

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

    private int Rollback(int[] nums, int target, int i, int value){
        if(i == nums.length){
            if(value == target) return 1;
            else return 0;
        }else{
            return Rollback(nums, target, i + 1, value + nums[i]) + Rollback(nums, target, i + 1, value - nums[i]);
        }
    }
}

2、动态规划
有点像0-1背包问题,如果每个元素都要考虑+和-,那么对于装包就有一定困难,可不可以考虑转换一下,只考虑+,那么就转成背包问题求解了。
记数组的元素和为sum,添加-号的元素和为neg,则添加+号的元素和为sum-neg,得到表达式结果为: ( s u m − n e g ) − n e g = t a r g e t (sum -neg)-neg = target (sumneg)neg=target,即 n e g = s u m − t a r g e t 2 neg=\frac{sum-target}{2} neg=2sumtarget
因为数组中均为非负整数,所以neg也必须是非负整数,因此 sum - target 必须是非负偶数,若不符合该条件直接返回0。
因此可以在数组中选取若干元素,其和为neg,使用动态规划方法。
为什么说找neg就行呢?因为对于和为neg的元素,前面给-号,其他的给+号,表达式结果就为target,因此只需要找到数组中和为neg的元素选取方案,就找到了原问题的结果。
定义二维数组dp, d p [ i ] [ j ] dp[i][j] dp[i][j]表示在数组nums的前 i 个数中选取元素,使得这些元素之和等于 j 的方案数。若数组长度为n,则最终结果为 d p [ n ] [ n e g ] dp[n][neg] dp[n][neg]
当没有任何元素可以选取时,元素和只能是0,对应有一种方案数,因此,动态规划的边界条件是:
d p [ 0 ] [ j ] = { 1 , j = 0 0 , j ≥ 0 dp[0][j] = \left \{ \begin{aligned} &1, & j =0 \\ & 0, & j \geq 0 \end{aligned} \right. dp[0][j]={1,0,j=0j0
1 < i ≤ n 1 < i \leq n 1<in时,对于数组nums中的第 i 个元素num(i从1开始计数),遍历 0 < j ≤ n e g 0 < j \leq neg 0<jneg,计算 d p [ i ] [ j ] dp[i][j] dp[i][j]的值:

  • j < n u m j < num j<num,说明不能选num,此时 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j] dp[i][j]=dp[i1][j]
  • j ≥ n u m j \geq num jnum,如果不选num,方案数是 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j],如果选num,方案数是 d p [ i − 1 ] [ j − n u m ] dp[i-1][j-num] dp[i1][jnum],此时有 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j − n u m s [ i ] ] dp[i][j]=dp[i-1][j] + dp[i-1][j-nums[i]] dp[i][j]=dp[i1][j]+dp[i1][jnums[i]]
    因此状态转移方程如下:
    d p [ i ] [ j ] = { d p [ i − 1 ] [ j ] , j < n u m s [ i ] d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j − n u m s [ i ] ] , j ≥ n u m s [ i ] dp[i][j] = \left \{ \begin{aligned} & dp[i-1][j] , & j < nums[i] \\ & dp[i-1][j] + dp[i-1][j-nums[i]], & j \geq nums[i] \end{aligned} \right. dp[i][j]={dp[i1][j],dp[i1][j]+dp[i1][jnums[i]],j<nums[i]jnums[i]
    结果值为 d p [ n ] [ n e g ] dp[n][neg] dp[n][neg]
class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        // 每个数都有要和不要两种状态,要就+
        // 设置dp[i][j],表示前i个数取元素,和为j的表达式数目
        int sum = 0;
        for(int num : nums)
            sum += num;
        int diff = sum - target;
        if(diff < 0 ||  diff % 2 != 0) return 0;

        int len = nums.length, neg = diff / 2;
        int[][] dp = new int[len + 1][neg + 1];
        dp[0][0] = 1;
        for(int i = 1; i <= len; i++){
            int num = nums[i - 1];
            for(int j = 0; j <= neg; j++){
                dp[i][j] = dp[i - 1][j];
                if(j >= num)
                    dp[i][j] += dp[i - 1][j - num];
            }
        }
        return dp[len][neg];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值