这是跟着代码随想录的顺序学习算法的第?天。
以下是学习时自己的一些理解与笔记,如有错误欢迎指正与讨论。
494.目标和
参考相关链接:
笔记
主要思路是将 nums
数组分成两部分来看待,一部分是取 +
号的,不妨将其总和记为left
,另一部分是取 -
号的 right
,将其总和记为 right
,考虑最终期望的得到的结果为 target
,可得等式 left - right = target
。
与此同时,我们不难发现等式 left + right = sum
,其中 sum
即为数组总和。
两个等式联立后可得 left = (sum + target)/2
,这样一来问题就转换成了在 nums
数组中能有多少种方法得到部分和为 left
的组合,这个问题可以用01背包来解决。
01背包中算法实现的一个核心思想为,在初始化了的基础上,考虑本次如果提前放入物品 i
,即在背包中提前占用 weight[i]
的容量,再去看此时的剩余容量中可以存放的最大价值为多少。
本题不需要知道最大价值,因为此处最大价值就是目标值 target
,只是需要知道有多少种方法可以达到这个目标值。因此,这里对原始的01背包做一些改动,即在占用提前占用了 nums[i]
容量后,再去看此时的剩余容量中,能够存放满剩余背包的方法有多少种,累加起来。
与此同时,初始化时也需要做出相应的修改,原始的01背包的 dp[j]
表示的是,在 j
容量下,能够装入的最大价值是多少,一开始没有装入的时候,最大价值自然就是 0
。此处的01背包的 dp[j]
表示的是在 j
容量下,能够装满的方法有多少种,一开始没有装入的时候,也就只有一种方法装满背包,即装入重量为 0
的,也就是不装。
var findTargetSumWays = function(nums, target) {
let sum = 0;
for(const x of nums) {
sum += x;
}
// 临界条件判断,背包需要达到的目标值必须是整数,target 需要在nums表示范围内
if((sum + target) % 2 || Math.abs(target) > sum) return 0;
const half = (sum + target) / 2;
// dp[j] 表示在 j 容量下,能有多少种方法使得总和达到 j
let dp = Array(half + 1).fill(0);
const len = nums.length;
// 初始化
dp[0] = 1;
for(let i = 0; i < len; i++) {
for(let j = half; j >= nums[i]; j--) {
// 现在可以装入物品 i,看看占用了nums[i] 容量后还有多少种方法可以达到 j - nums[i]
dp[j] += dp[j - nums[i]];
}
}
return dp[half];
};