【回溯-7】494.目标和

题目描述:

给你一个非负整数数组 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

解题思路:

方法一:回溯(DFS)

思路:

对于每个数字,有两种选择:加 + 或加 -。

  • 递归到最后一个数字时,判断和是否等于 target

  • 是则计数 +1

代码实现:

cpp 复制代码
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int count = 0;
        backtrack(nums, target, 0, 0, count);
        return count;
    }
    
private:
    void backtrack(vector<int>& nums, int target, int index, int sum, int& count) {
        if (index == nums.size()) {
            if (sum == target) count++;
            return;
        }
        
        // 选择 +
        backtrack(nums, target, index + 1, sum + nums[index], count);
        // 选择 -
        backtrack(nums, target, index + 1, sum - nums[index], count);
    }
};

复杂度分析:

维度 复杂度 说明
时间复杂度 O(2^n) 每个数字两种选择
空间复杂度 O(n) 递归栈深度

缺点:数据量大时超时。

方法二:动态规划(转化为 01 背包)

核心转化:

设所有数字的和为 sum,加 + 的数字和为 P,加 - 的数字和为 N。

复制代码
P + N = sum
P - N = target

两式相加:2P = sum + target,即:

复制代码
P = (sum + target) / 2

问题转化为 :从 nums 中选一些数字,使它们的和等于 P,求方案数。

这就是 01 背包问题!

代码实现:

cpp 复制代码
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for (int num : nums) sum += num;
        
        // 无法整除,或目标绝对值超过总和
        if ((sum + target) % 2 != 0 || abs(target) > sum) return 0;
        
        int P = (sum + target) / 2;
        
        // dp[j] = 和为 j 的方案数
        vector<int> dp(P + 1, 0);
        dp[0] = 1;
        
        for (int num : nums) {
            for (int j = P; j >= num; j--) {
                dp[j] += dp[j - num];
            }
        }
        
        return dp[P];
    }
};

具体过程示例:

nums = [1,1,1,1,1], target = 3

复制代码
sum = 5
P = (5 + 3) / 2 = 4

问题转化为:从 [1,1,1,1,1] 中选数字,和为 4 的方案数

dp[0] = 1
遍历每个 1:
  dp[1] += dp[0] → dp[1] = 1
  dp[2] += dp[1] → dp[2] = 1
  dp[3] += dp[2] → dp[3] = 1
  dp[4] += dp[3] → dp[4] = 1

遍历第二个 1:
  dp[1] += dp[0] → dp[1] = 2
  dp[2] += dp[1] → dp[2] = 3
  dp[3] += dp[2] → dp[3] = 4
  dp[4] += dp[3] → dp[4] = 5

...最终 dp[4] = 5 ✅

复杂度分析:

维度 复杂度 说明
时间复杂度 O(n × P) n 个数字,P 是目标和
空间复杂度 O(P) dp 数组

方法三:记忆化搜索

思路:

在回溯的基础上,用哈希表记录 (index, sum) 的结果,避免重复计算。

代码实现:

cpp 复制代码
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        unordered_map<string, int> memo;
        return dfs(nums, target, 0, 0, memo);
    }
    
private:
    int dfs(vector<int>& nums, int target, int index, int sum,
            unordered_map<string, int>& memo) {
        if (index == nums.size()) {
            return sum == target ? 1 : 0;
        }
        
        string key = to_string(index) + "," + to_string(sum);
        if (memo.count(key)) return memo[key];
        
        int count = dfs(nums, target, index + 1, sum + nums[index], memo)
                  + dfs(nums, target, index + 1, sum - nums[index], memo);
        
        memo[key] = count;
        return count;
    }
};

复杂度分析:

维度 复杂度 说明
时间复杂度 O(n × sum) 每个 (index, sum) 只算一次
空间复杂度 O(n × sum) 哈希表

三种方法对比:

方法 时间复杂度 空间复杂度 推荐度
回溯 O(2^n) O(n) ⭐⭐
动态规划(01背包) O(n × P) O(P) ⭐⭐⭐⭐⭐
记忆化搜索 O(n × sum) O(n × sum) ⭐⭐⭐⭐

总结:

要点 说明
核心思想 转化为 01 背包:选一些数字和为 P
关键公式 P = (sum + target) / 2
时间复杂度 O(n × P)
空间复杂度 O(P)
相关推荐
春弦_21 小时前
自用算法整理二
数据结构·算法·图论
hetao17338371 天前
2026-10-22~23 hetao1733837 的刷题记录
c++·算法
java_nnnn1 天前
LeetCode刷题滑动窗口(串联所有单词的子串+最小覆盖的子串)Java
算法·leetcode·职场和发展
王老师青少年编程1 天前
2025年6月GESP真题及题解(C++三级): 选择题和判断题(题解)
c++·真题·gesp·答案·csp·三级·信奥赛
Navigator_Z1 天前
LeetCode //C - 1301. Number of Paths with Max Score
c语言·算法·leetcode
卡卡~~1 天前
“101计划”核心课程实践教材《数据实验结构指导-C++语言版》题目集
开发语言·c++·学习·算法
企业数字化笔记1 天前
AI视频增强后画质怎么验收?插值基线、伪细节与帧间闪烁的检查方法
人工智能·算法·音视频
学逆向的1 天前
C 内存属性
c语言·开发语言·算法·网络安全
lengxuenong1 天前
树状数组 · 信息学学习手册1-求和3个模板
c++·学习·算法·青少年编程