[路飞]_494.目标和

397 阅读2分钟

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战

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

题解

暴力搜索

枚举 numsnums 数组;数组内任意元素执行加法或者减法,将计算结果与数组下一位元素在进行加法或或者减法;这个思路就递归;

如果数组内所有元素已经执行完,如果数组内所有元素计算结果为 SS ;完成目标值表达式数目+1

时间复杂度:O(2n)O(2^n)

var findTargetSumWays = function (nums, S) {
  let result = 0;
  dfs(0, 0);
  return result;
  function dfs(idx, t) {
    if (idx === nums.length) {
      if (t === S) result++;
      return;
    }

    dfs(idx + 1, t + nums[idx]);
    dfs(idx + 1, t - nums[idx]);
  }
};

动态规划

暴力搜索时间复杂度达到 O(2n)O(2^n) ;时间复杂度太高了,在搜索过程中会有很多重复计算。加入这些重复计算可以保留下来,是不是就可以降低时间复杂度

可以将 dp[ i ][ j ]定义为从数组 nums 中 0 - i 的元素进行加减可以得到 j 的方法数量

举个例子:nums=[2,3,1,4,5]nums = [2,3,1,4,5]

  • 数组总和 2+3+1+4+5=15 2+3+1+4+5 = 15
    • 数组串联起来所有可能的表达式在区间[15,15][-15,15]上,
    • 所以在 dp[i][j]dp[ i ][ j ] 数组中,ii的区间是[0,4][0,4],jj的区间是[15,15][-15,15],
    • 又因为数组下标没有负数,所以需要将数组右移 [15,15]+15=>[0,30] [-15,15] + 15 = > [0,30]
  • dp[0][0]=1dp[0][0] = 1 ,解释:00个数,表达式结果为00的方案数为11
  • dp[1][0]dp[1][1]...dp[1][30]dp[1][0],dp[1][1],...,dp[1][30],解释:11个数,计算表达式结果为[0,30][0,30] 区间任意数的可能,并记录到 dpdp
  • dp[2][0]dp[2][1]...dp[2][30]dp[2][0],dp[2][1],...,dp[2][30],解释:22个数,计算表达式结果为[0,30][0,30] 区间任意数的可能,并记录到 dpdp

因为每个数值只能加或者减,是不是可以得到状态转移方程:
dp[i][j]=dp[i1][jnums[i1]]+dp[i1][j+nums[i1]]dp[i][j]=dp[i−1][j−nums[i−1]]+dp[i−1][j+nums[i−1]]

根据状态方程编辑代码如下:

var findTargetSumWays = function (nums, S) {
  const len = nums.length;
  const t = nums.reduce((a, b) => Math.abs(a) + Math.abs(b));
  //动态规划
  const dp = [];
  for (let i = 0; i <= len; i++) {
    dp[i] = Array(2 * t + 1).fill(0);
  }
  if (Math.abs(S) > Math.abs(t)) return 0;

  dp[0][0 + t] = 1;

  for (let i = 1; i <= len; i++) {
    const c = nums[i - 1];
    for (let j = -t; j <= t; j++) {
      if (j - c + t >= 0) {
        dp[i][j + t] += dp[i - 1][j - c + t];
      }
      if (j + c + t <= 2 * t) {
        dp[i][j + t] += dp[i - 1][j + c + t];
      }
    }
  }
  //console.log('dp', dp)
  return dp[len][S + t];
};