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
提示:
-
1 <= nums.length <= 20
-
0 <= nums[i] <= 1000
-
0 <= sum(nums[i]) <= 1000
-
-1000 <= target <= 1000
解题思路
假设符号为+
于是题目就可以转换为:装满容量为left
的背包,最多有几种方法。这可以用01背包来解。
首先,我们要两种情况需要先排除:
-
当
target
的绝对值大于sum
时,不可能实现 -
从公式
2*left = target + sum
可以看出,2*left
一定时偶数,所以target + sum
也必须保证时偶数。
使用一维数组的解题步骤:
-
确定dp数组以及其下标的含义
dp[j]
表示装满背包容量为j的有多少种方法 -
确定递推公式
不考虑
nums[i]
的情况下,填满容量为j - nums[i]
的背包,有dp[j - nums[i]]
种方法。也就是当前填满容量为
j
的背包的方法数 = 之前填满容量为j
的背包的方法数 + 之前填满容量为j - nums[i]
的方法数。于是得到递推公式为:dp[j] = dp[j] + dp[j - nums[i]]
-
dp数组的初始化
用0件物品(在下面的代码中可以看到遍历物品的i是从下标0开始的,所以此时并没有物品)装满背包容量为0的背包,有一种方法。
所以
dp[0] = 1
-
确定遍历顺序
一维dp解决0-1背包问题,先正序遍历物品,再倒叙遍历背包。
-
举例推导dp数组(略)
C++
class Solution { public: int findTargetSumWays(vector<int>& nums, int target) { int sum = 0; for (int i = 0; i < nums.size(); i++) { sum += nums[i]; } if (abs(target) > sum || ( target + sum ) % 2 == 1) { return 0; } int bagSize = ( sum + target ) / 2; vector<int> dp(bagSize + 1, 0); dp[0] = 1; for (int i = 0; i < nums.size(); i++) { for (int j = bagSize; j >= nums[i]; j--) { dp[j] += dp[j - nums[i]]; } } return dp[bagSize]; } };
JavaScript
/** * @param {number[]} nums * @param {number} target * @return {number} */ var findTargetSumWays = function(nums, target) { let sum = 0; for (let i = 0; i < nums.length; i++) { sum += nums[i]; } if (Math.abs(target) > sum || (sum + target) % 2 == 1) { return 0; } let bagSize = (sum + target) / 2; const dp = new Array(bagSize + 1).fill(0); dp[0] = 1; for (let i = 0; i < nums.length; i++) { for (let j = bagSize + 1; j >= nums[i]; j--) { dp[j] += dp[j - nums[i]]; } } return dp[bagSize]; };
-
时间复杂度:O(n × m),n为正数个数,m为背包容量
-
空间复杂度:O(m)
另外,还可以用二维数组解决。不过需要注意dp数组的初始化,因为nums[0] = 0
是一种特殊的情况。给出C++代码如下。
class Solution { public: int findTargetSumWays(vector<int>& nums, int target) { int sum = 0; for (int i = 0; i < nums.size(); i++) { sum += nums[i]; } if (abs(target) > sum || ( target + sum ) % 2 == 1) { return 0; } int bagSize = ( sum + target ) / 2; vector<vector<int>> dp(nums.size(),vector<int>(bagSize + 1, 0)); // 特殊!!!!!!!!!!!!!!!!!!!!!! // 因为 0 既可以用 -0 表示, 也可以用 +0 表示 // 所以将 0 (不是物品的下标,而是物品的重量或价值) 填满容量背包为 0 的背包中,有两种方法 if (nums[0] == 0) { dp[0][0] = 2; } else { dp[0][0] = 1; } for (int j = 1; j < bagSize + 1; j++) { if (j == nums[0]) dp[0][j] = 1; } for (int i = 1; i < nums.size(); i++) { for (int j = 0; j < bagSize + 1; j++) { if (j < nums[i]) { dp[i][j] = dp[i - 1][j]; } else { dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]]; } } } return dp[nums.size() - 1][bagSize]; } };
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理