【温故知新】`494. 目标和` 表达式转化为01背包🎒问题-动态规划实现

792 阅读2分钟

题目描述:

给你一个整数数组 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 <= 1000

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/ta… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

image.png

01背包🎒问题转换:

本题的难点是01背包问题的转换:

01背包问题是选或者不选,在本题中时nums数组元素必选,而选或者不选的是+或者-号;

  • 先将本题转换为01背包问题;
  • 假设满足要求的表达式中:所有符号为+的元素之和为x;所有符号为-的元素之和的绝对值为y;
  • 则我们想要的结果target=x-y;
  • 而已知x和y之和即为数组之和sum=x+y;
  • 由以上两个公式求得x=(target+sum)/2
  • 将问题转换为nums中挑选出几个数其和为x;
    • 转化为01背包🎒装满问题:容量为x的,物品为nums数组的01背包🎒问题; 动态规划四部曲:
  • 确定dp含义
    • dp数组定义:dp[j]代表填满容量为j的背包🎒总共有dp[j]种方法;
  • 确定dp公式:dp[j] = dp[j] + dp[j - num],
  • dp数组初始化:
    • 初始化容量为x的dp数组为0;
    • 且dp[0]=1;实际含义:填满容量为0的背包只有1中方法;
  • 遍历顺序:
    • 01背包一定是外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!

时空复杂度

  • 时间复杂度:O(n * x) 两层for循环;n为nums.length;
  • 空间复杂度:O(x))

代码

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var findTargetSumWays = function(nums, target) {
  
  let sum=0;
  for(let num of nums){
      sum+=num
  }
  // 当目标值大于数组元素的最大值,即所有元素选择+的情况;
  if(target>sum){
      return 0;
  }
  // 如果x不是整数,即(target+sum)不是偶数,则不可能实现;
  if((target+sum)%2==1){
      return 0;
  }
  //dp数组定义:dp[j]代表填满容量为j的背包🎒总共有dp[j]种方法;
  // 最大容量x
  let x=Math.floor((target+sum)/2);
    // dp初始化;dp[0]=1,实际含义:填满容量为0的背包只有1中方法;
  let dp=new Array(x+1).fill(0);
  dp[0]=1;
  // 递推公式dp[j]=dp[j]+dp[j-nums[i]]
  for(let num of nums){
      for(let j=x;j>=num;j--){
          dp[j]=dp[j]+dp[j-num]
      }
  }// end of for
  return dp[x]

};