题目描述
给你一个非负整数数组 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
/**
* 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];
};
实际应用场景
- 金融投资组合:计算达到目标收益的资产组合方案数`
- 游戏策略设计:角色属性点分配达到特定目标的方案`
- 电路设计:电阻网络达到目标阻抗的配置方案`
- 药物剂量组合:不同剂量组合达到目标疗效的方案`