608 字
3 分钟
目标和

题目描述#

给你一个非负整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :

  • 例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。

返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

解题思路#

  1. DFS回溯:递归遍历数组,对每个数字选择 + 或 -
  2. 终止条件:遍历完数组时,若当前和 == target,计数 +1
  3. 剪枝优化:记录剩余数字和,若剩余和 < 目标差 则提前返回。

关键洞察#

  1. 数学转换:问题等价于将数组拆分为两个子集,使两子集和之差为 target
  2. 状态复用:动态规划中,dp[j] 表示和为 j 的方案数,避免重复计算。
  3. 偏移处理:负数和时需用偏移量转换下标,确保 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 (numstarget) {
    let res = 0;
    const dfs = (isum=> {
        if (i === nums.length) {
            if (sum === target) {
                res++;
            }
            return;
        }
        dfs(i + 1, sum + nums[i]);
        dfs(i + 1, sum - nums[i]);
    }
    dfs(00);
    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];
};

实际应用场景#

  1. 金融投资组合:计算达到目标收益的资产组合方案数`
  2. 游戏策略设计:角色属性点分配达到特定目标的方案`
  3. 电路设计:电阻网络达到目标阻抗的配置方案`
  4. 药物剂量组合:不同剂量组合达到目标疗效的方案`

相似题目#

目标和
https://website-truelovings-projects.vercel.app/posts/algorithms/目标和/
作者
欢迎来到StarSky的网站!
发布于
2025-08-06
许可协议
CC BY-NC-SA 4.0