608 字
3 分钟
目标和

题目描述
给你一个非负整数数组 nums
和一个整数 target
。
向数组中的每个整数前添加 '+'
或 '-'
,然后串联起所有整数,可以构造一个 表达式 :
- 例如,
nums = [2, 1]
,可以在2
之前添加'+'
,在1
之前添加'-'
,然后串联起来得到表达式"+2-1"
。
返回可以通过上述方法构造的、运算结果等于 target
的不同 表达式 的数目。
解题思路
- DFS回溯:递归遍历数组,对每个数字选择
+
或-
。 - 终止条件:遍历完数组时,若当前和
== target
,计数+1
。 - 剪枝优化:记录剩余数字和,若剩余和
< 目标差
则提前返回。
关键洞察
- 数学转换:问题等价于将数组拆分为两个子集,使两子集和之差为
target
。 - 状态复用:动态规划中,
dp[j]
表示和为j
的方案数,避免重复计算。 - 偏移处理:负数和时需用偏移量转换下标,确保 DP 数组有效。
复杂度分析
- 时间复杂度:
- 回溯:O(2ⁿ)(最坏)
- DP:O(n·range),
range = sum(nums)
- 空间复杂度:
- 回溯:O(n)(递归栈)
- DP:O(range)
代码实现
/** * 回溯法 * @param {number[]} nums * @param {number} target * @return {number} */var findTargetSumWays = function (nums, target) { let res = 0; const dfs = (i, sum) => { if (i === nums.length) { if (sum === target) { res++; } return; } dfs(i + 1, sum + nums[i]); dfs(i + 1, sum - nums[i]); } dfs(0, 0); return res;};
/** * DP * @param {number[]} nums * @param {number} target * @return {number} */var findTargetSumWays = function(nums, target) { let sum = 0; for (const num of nums) { sum += num; } const diff = sum - target; if (diff < 0 || diff % 2 !== 0) { return 0; } const n = nums.length, neg = diff / 2; const dp = new Array(n + 1).fill(0).map(() => new Array(neg + 1).fill(0)); dp[0][0] = 1; for (let i = 1; i <= n; i++) { const num = nums[i - 1]; for (let 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[n][neg];};
实际应用场景
- 金融投资组合:计算达到目标收益的资产组合方案数`
- 游戏策略设计:角色属性点分配达到特定目标的方案`
- 电路设计:电阻网络达到目标阻抗的配置方案`
- 药物剂量组合:不同剂量组合达到目标疗效的方案`