Skip to content

Latest commit

 

History

History
165 lines (133 loc) · 3.75 KB

494. 目标和.md

File metadata and controls

165 lines (133 loc) · 3.75 KB

给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 +-。对于数组中的任意一个整数,你都可以从 +-中选择一个符号添加在前面。

返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

示例:

输入:nums: [1, 1, 1, 1, 1], S: 3
输出:5
解释:

-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

一共有5种方法让最终目标和为3。

提示:

  • 数组非空,且长度不会超过 20 。
  • 初始的数组的和不会超过 1000 。
  • 保证返回的最终结果能被 32 位整数存下。

思路

1、暴力枚举, DFS

2、动态规划

代码

/**
 * @param {number[]} nums
 * @param {number} S
 * @return {number}
 */
var findTargetSumWays = function(nums, S) {
  const sum = nums.reduce((total, cur) => total + cur, 0);
  const len = nums.length;

  let ret = 0;
  if (Math.abs(sum) < Math.abs(S)) return ret;

  function dfs(idx, sum) {
    if (idx === len) {
      if (sum === S) {
        ret ++
      }
      return;
    } else {
      dfs(idx + 1, sum + nums[idx])
      dfs(idx + 1, sum - nums[idx])
    }
  }

  dfs(0, 0);

  return ret;
};
/**
 * @param {number[]} nums
 * @param {number} S
 * @return {number}
 */
var findTargetSumWays = function(nums, S) {
  // 计算和
  const sum = nums.reduce((total, cur) => total + cur, 0);
  // 获取dp数组每一子项的长度为 sum*2+1
  const len = nums.length, itemLen = sum * 2 + 1;

  let ret = 0;
  // 不可能存在
  if (Math.abs(sum) < Math.abs(S)) return ret;

  const dp = Array(len)
  dp[0] = Array(itemLen).fill(0);
  // 考虑到第一个数为0的情况,+0,-0 有2种
  if (nums[0] === 0) {
    dp[0][sum] = 2
  } else {
    // 将nums[0]用上能够出现的情况
    dp[0][sum - nums[0]] = 1
    dp[0][sum + nums[0]] = 1
  }

  for(let i = 1; i < len; i ++) {
    let cur = nums[i];
    // 初始化其他的dp数组
    dp[i] = Array(itemLen).fill(0)
    for(let j = 0; j < itemLen; j ++) {
      // 边界,如果越界则表示不能满足条件
      let left = (j - nums[i] >= 0) ? j - nums[i] : 0;
      let right = (j + nums[i] < itemLen) ? j + nums[i] : 0;
      dp[i][j] = dp[i-1][left] + dp[i-1][right]
    }
  }
  // 返回目标值 
  return dp[len-1][S + sum]
};
/**
 * @param {number[]} nums
 * @param {number} S
 * @return {number}
 */
// 因为每一行只和前一行的状态有关系,所以可以缩小空间复杂度
var findTargetSumWays = function(nums, S) {
  // 计算和
  const sum = nums.reduce((total, cur) => total + cur, 0);
  // 获取dp数组每一子项的长度为 sum*2+1
  const len = nums.length, itemLen = sum * 2 + 1;

  let ret = 0;
  // 不可能存在
  if (Math.abs(sum) < Math.abs(S)) return ret;

  const dp = Array(len)
  dp[0] = Array(itemLen).fill(0);
  // 考虑到第一个数为0的情况,+0,-0 有2种
  if (nums[0] === 0) {
    dp[0][sum] = 2
  } else {
    // 将nums[0]用上能够出现的情况
    dp[0][sum - nums[0]] = 1
    dp[0][sum + nums[0]] = 1
  }

  for(let i = 1; i < len; i ++) {
    let cur = nums[i];
    // 创建临时数组用来存储当前行的情况
    let tmp = Array(itemLen).fill(0)
    for(let j = 0; j < itemLen; j ++) {
      // 边界
      let left = (j - nums[i] >= 0) ? j - nums[i] : 0;
      let right = (j + nums[i] < itemLen) ? j + nums[i] : 0;
      tmp[j] = dp[left] + dp[right]
    }
    // 将当前行计算完情况后重新赋值回去
    dp = tmp;
  }
  // 返回
  return dp[S + sum]
};

复杂度分析

时间复杂度 DFS:$O(2^N)$ DP: $O(N^2)$ DP2: $O(N^2)$

空间复杂度 DFS: $O(N)$ DP: $O(N^2)$ DP2: $O(N)$