【LeetCode】494. 目标和

题目链接:目标和

题目描述:

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

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

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

分析:数组中的每一个数字都要用到,而且每一个数字都有两种情况,+或者-。

方法一:回溯(递归,dfs)O(2^n)

代码如下:

var findTargetSumWays = function (nums, target) {
    let count = 0;
    const backtrack = function (nums, target, index, sum) {
        if (index === nums.length) {
            if (sum === target)
                count++;
        } else {
            // 加
            backtrack(nums, target, index + 1, sum + nums[index]);
            // 减
            backtrack(nums, target, index + 1, sum - nums[index]);
        }
    }
    backtrack(nums, target, 0, 0);
    return count;
};

方法二:记忆化搜索

在上述回溯(递归)的过程中,对于相同的<index,sum>,之后的递归过程都是一样的,所以就可以把某一个<index,sum>对应的结果存储起来,之后再遍历到的时候直接返回值即可。

使用map进行存储,key为字符串index+"_"+sum,value是对应的方案数。

即将过程中得到的结果记录下来,再次需要的时候直接使用。就能够减少一些递归过程,提升性能。

代码如下:

var findTargetSumWays = function (nums, target) {
    const map = new Map();
    const backtrack = function (nums, target, index, sum) {
        let key = index + "_" + sum;
        if (map.has(key))
            return map.get(key);
        if (index === nums.length) {
            map.set(key, sum === target ? 1 : 0)
            return map.get(key);
        }
        // 加
        let add = backtrack(nums, target, index + 1, sum + nums[index]);
        // 减
        let sub = backtrack(nums, target, index + 1, sum - nums[index]);
        map.set(key, add + sub);
        return map.get(key);
    }

    return backtrack(nums, target, 0, 0);
};

方法三:动态规划

数组中所有元素的和为sum,为了构成target,符号为-的元素的和为diff,那么符号为+的元素的和为sum-diff,由题意可得,(sum-diff)-diff=sum-2*diff=target,即diff=(sum-target)/2。

那么求构成target的方案数,显然就等价于求几个元素的和为diff的方案数。

问题转换成了,从n个数中选择若干个元素,使其和为diff的方案数

可以使用动态规划,dp[i][j]表示从nums的前i-1个数中选取若干个数,使其和为j的方案数。

边界条件是:dp[0][0]=1,

最终目标为:dp[n][diff]

状态转移方程为:

1)若sum-target是负数或奇数,那么直接返回0即可。

2)令nums[i-1]为num,

        a。若num>j,那么num当然是不可取的,此时dp[i][j]=dp[i-1][j]

        b。若num<=j,那么num可取可不取,即dp[i][j]=dp[i-1][j]+dp[i-1][j-num]

代码如下:

var findTargetSumWays = function (nums, target) {
    const length = nums.length;
    let sum = 0;
    for (const num of nums)
        sum += num;
    if (sum < target || (sum - target) % 2 === 1)
        return 0;
    const diff = (sum - target) / 2;
    // 二维数组初始化
    const dp = new Array(length + 1).fill(0).map(() => new Array(diff + 1).fill(0));
    dp[0][0] = 1;
    for (let i = 1; i <= length; i++) {
        let num = nums[i - 1];
        for (let j = 0; j <= diff; j++) {
            dp[i][j] = dp[i - 1][j];
            if (j >= num)
                dp[i][j] += dp[i - 1][j - num];
        }
    }
    return dp[length][diff];
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值