问题描述:
You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols +
and -
. For each integer, you should choose one from +
and -
as its new symbol.
Find out how many ways to assign symbols to make sum of integers equal to target S.
Example 1:
Input: nums is [1, 1, 1, 1, 1], S is 3. Output: 5 Explanation: -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 There are 5 ways to assign symbols to make the sum of nums be target 3.
Note:
- The length of the given array is positive and will not exceed 20.
- The sum of elements in the given array will not exceed 1000.
- Your output answer is guaranteed to be fitted in a 32-bit integer.
问题来源:Target Sum
解题分析:
题意是让在一个数组中的一些数之前添加“+”,其它的数之前添加“-”,从而让数组之和达到给定的数。
我们将添加“+”的数放入集合P,其它的数放入集合N,于是我们有:
- sum(P) - sum(N) = target
- sum(P) + sum(N) = sum
于是有
sum(P) = (target + sum) / 2
,那么不妨这样理解题意,从一个数组中选定一些数,使它们的和为sum(P),如此就变成了很经典的0/1背包问题,从一个n大小的背包中选出总和为sum(P)的方案个数。
状态表示:dp[i][j]代表前i个数中和为j的方案个数。
状态转移方程:dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]],dp[0][0] = 1
返回结果:dp[n][target],n为数组大小,target为sum(P
)。
如此时间复杂度为O(N^2),空间复杂度为O(M*N)。
源代码:
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
int sum = 0;
for (auto n : nums) {
sum += n;
}
/*判断数组中的元素的和是否能达到目标数*/
if ((sum + S) % 2 == 1 || sum < S || S < -sum) {
return 0;
}
int target = (sum + S) / 2;
vector<int> dp(target + 1, 0);
/*相同于初始化dp[0][0] = 1*/
dp[0] = 1;
for (int i = 0; i < nums.size(); i++) {
for (int j = target; j >= nums[i]; j--) {
/*利用滚动数组循环更新dp[j],其效果等同于dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]]
*空间复杂度优化至O(M)。
*/
dp[j] += dp[j - nums[i]];
}
}
return dp[target];
}
};