【回溯-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)
相关推荐
白杨尚青1 小时前
C++入门篇(十三):vector(上)——动态数组:构造、空间增长与迭代器失效
开发语言·c++·笔记·算法·stl
Jasmine_llq1 小时前
《P17461 [GESP202609 八级] 生成树计数》
算法·栈·tarjan 算法·边双连通分量·深度优先搜索 dfs·tarjan 保存当前路径节点·模乘法
Jasmine_llq1 小时前
《P17462 [GESP202609 八级] 末班车》
算法·dijkstra 算法·最大化版本,求最长可行值·建反向图(逆图 / 反转边)·多源 dijkstra 思想·对每个终点单独跑一遍djst·离线预处理 + 在线查询
50万马克的面包1 小时前
数据结构01-数据结构概念与复杂度-博客稿
数据结构
Logic1011 小时前
C语言/数据结构贪心算法题解:字符串趋同——逐位统计最高频率求最小修改代价
c语言·数据结构·贪心算法·字符串·时间复杂度·频率统计·算法题
the3clipse2 小时前
H.265熵编码核心:CABAC自适应二进制算术编码详解——如何将语法元素高效压缩为比特流
人工智能·算法·视频编码·h.265·hevc·cabac·cavlc
CQU_JIAKE2 小时前
10.4【A】
算法
Godspeed Zhao2 小时前
现代智能雷达技术19——算法(5)
算法
(Charon)2 小时前
【C++面试】RAII到底是什么:从资源管理理解C++对象生命周期
c++·算法·面试