题目描述
给定一个非负整数序列,a_1, a_2, ..., a_n
,和一个目标值 S
。现在你有两种符号 +
和 -
。对于每个整数,你可以选择为其选择一个符号。
找到有多少种添加符号的方式使其目标值等于 S
。
样例
输入: nums: [1, 1, 1, 1, 1], S: 3
输出: 5
解释:
-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
一共有 5 种方法让最终目标和为 3。
注意
- 数组非空,且长度不会超过
20
。 - 初始的数组的和不会超过
1000
。 - 保证返回的最终结果能被 32 位整数存下。
算法
(动态规划) O(n⋅sum)
- 定义 f(i,j) 表示添加了前 i 个数字的符号,得到总和为 j 的方案数。
- 对于第 i 个符号,f(i,j)=f(i−1,j+nums[i])+f(i−1,j−nums[i])。
- j 的范围是 [−sum,sum],sum 为数组元素的总和。
- 初始值 f(0,0)=1,最终答案为 f(n,S)。
- 为了方便,第一维的下标从 1 开始;第二维也需要给 sum 的偏移量防止负下标。
时间复杂度
- 状态数为 O(n⋅sum),转移数和转移时间为 O(1),故总时间复杂度为 O(n⋅sum)。
空间复杂度
- 需要额外 O(n⋅sum) 的空间存储状态。
C++ 代码
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
int n = nums.size();
int sum = accumulate(nums.begin(), nums.end(), 0);
if (!(-sum <= S && S <= sum))
return 0;
vector<vector<int>> f(n + 1, vector<int>(2 * sum + 1, 0));
f[0][0 + sum] = 1;
for (int i = 1; i <= n; i++)
for (int j = -sum; j <= sum; j++) {
if (-sum <= j - nums[i - 1])
f[i][j + sum] += f[i - 1][j - nums[i - 1] + sum];
if (j + nums[i - 1] <= sum)
f[i][j + sum] += f[i - 1][j + nums[i - 1] + sum];
}
return f[n][S + sum];
}
};
为什么不能合并循环内f[i][j]的判断直接写
if (-sum <= j - nums[i - 1] && j + nums[i - 1] <= sum) f[i][j + sum] += f[i - 1][j - nums[i - 1] + sum] + f[i - 1][j + nums[i - 1] + sum];
if A then doA();
if B then doB();
和
if A and B then doA(); doB();
是有区别的
如果 if A and not B 和 if B and not A 的情况在后者就丢失了
谢谢大佬,懂了!
多谢大佬,不过 初始值 f(0,0)=0,这个地方写错了吧,初始值为f(0, 0) = 1;
已修正~
判断可以简化一下:
if (-sum > S || sum < S)
数据弱,global里开个大数组一把梭
class Solution { public: int dp[30][4020]; inline int loc(int n){ return n+2000; } int findTargetSumWays(vector<int>& nums, int S) { dp[0][loc(0)] =1; int l=nums.size(); int sum=0; for (auto i: nums) sum += abs(i); if (sum < S) return 0; for (int i = 1; i<=l; i++){ int x = nums[i-1]; for (int j= abs(sum); j>=-abs(sum); j--) dp[i][loc(j)] +=(dp[i-1][loc(j-x)] + dp[i-1][loc(j+x)]); } return dp[l][loc(S)]; } };
当 j = -sum 的时候,f[i][j + sum] += f[i - 1][j - nums[i - 1] + sum]; 这个不会越界吗???
有个 if 判断防止越界呀
想请教一下这里的偏移量具体是怎样防止负下标的,有点不太明白。。
举个例子,比如数组的范围是 [-100, 100],由于C++不支持负下标,所以需要定义一个偏移量 sum = 100,将数组范围定义为 [0, 200]