Leetcode: 目标和

22 阅读2分钟

原题:leetcode.cn/problems/ta…

题目描述

给你一个非负整数数组 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 {numbertarget@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(00);
    return res;
};
  • DP
/**
 * DP
 * @param {number[]nums@param {numbertarget@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. 药物剂量组合:不同剂量组合达到目标疗效的方案`

相似题目