法1:动态规划
看了题解
想法:
- 思考:什么是背包?什么是物品?
- 将nums分为P(加+)和N(加-)两部分
- sums( P ) + sums( N ) = sums(nums)
- sums( P ) - sums( N ) = S
- => sums( P ) = (sums(nums) + S) / 2
- 则只需要选取数组中元素加入背包(这些数前面加+),使得背包正好被装满;求装满的方法数
- 数组dp表示:选取nums中元素加入背包中,元素之和等于 i 的选取的方法数
/**
* @param {number[]} nums
* @param {number} S
* @return {number}
*/
var findTargetSumWays = function(nums, S) {
// 思考:什么是背包?什么是物品?
// 将nums分为P(加+)和N(加-)两部分
// sums(P) + sums(N) = sums(nums)
// sums(P) - sums(N) = S
// => sums(P) = (sums(nums) + S) / 2
// 则只需要选取数组中元素加入背包(这些数前面加+),使得背包正好被装满;求装满的方法数
var dp = [], i = 0, j = 0, sum = 0, W = 0;
// dp表示有几种方式使得和为i;W为背包容量
for(i = 0; i < nums.length; i++) {
sum += nums[i];
}
W = (sum + S) / 2;
// 如果W不是整数,则方法数为0
// 若目标和大于全部整数为正时候的和,则方法数为0
if(Math.floor(W) != W || S > sum) {
return 0;
}
for(i = 0; i <= W; i++) {
dp[i] = 0;
}
dp[0] = 1; // 容量为0的背包:不在背包里放东西,则能够使得背包“装满” => 一种方法数
for(i = 0; i < nums.length; i++) { // 对于nums中每个数字(物品)
for(j = W; j >= nums[i]; j--) {
dp[j] += dp[j - nums[i]]; // dp[j - nums[i]]:将当前物品装入之后,剩余空间的可能组合数
}
}
return dp[W];
};
法2:递归
看了题解
想法:
- 递归结束条件:若数组为空,则:S == 0时,方法数为1,否则方法数为0
- 对于数组的第一个数nums[0],有可能在前面加 - 或 +,则:
- S = -nums[0] + 剩余的数 => S + nums[0] = 剩余的数
- S = +nums[0] + 剩余的数 => S - nums[0] = 剩余的数
上面两种可能性,表示父节点的两个子节点(两种走向)
/**
* @param {number[]} nums
* @param {number} S
* @return {number}
*/
var findTargetSumWays = function(nums, S) {
// 递归结束条件:若数组为空,则:S == 0时,方法数为1,否则方法数为0
// 对于数组的第一个数nums[0],有可能在前面加 - 或 +,则:
// S = -nums[0] + 剩余的数
// S + nums[0] = 剩余的数
// S = +nums[0] + 剩余的数
// S - nums[0] = 剩余的数
// 上面两种可能性,表示父节点的两个子节点(两种走向)
return findTargetSumWays2(nums, 0, S);
};
function findTargetSumWays2(nums, start, S) {
if(start == nums.length) {
return S == 0 ? 1 : 0;
}
return findTargetSumWays2(nums, start + 1, S + nums[start]) +
findTargetSumWays2(nums, start + 1, S - nums[start]);
}