@@ -825,30 +825,69 @@ func abs(x int) int {
825825
826826### JavaScript
827827``` javascript
828+ /**
829+ * 题目来源: {@link https://leetcode.cn/problems/target-sum/ }
830+ *
831+ * 题解来源: {@link https://programmercarl.com/0494.%E7%9B%AE%E6%A0%87%E5%92%8C.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE }
832+ *
833+ * 时间复杂度: O(n * C), C 为数组元素总和与目标值之和的一半
834+ *
835+ * 空间复杂度: O(C)
836+ *
837+ * @param { number[] } nums
838+ * @param { number } target
839+ * @return { number }
840+ */
828841const findTargetSumWays = (nums , target ) => {
829-
830- const sum = nums .reduce ((a , b ) => a+ b);
842+ // 原题目可转化为:
843+ //
844+ // 将所有元素划分为 2 个集合,
845+ // 一个集合中包含所有要添加 "+" 号的元素, 一个集合中包含所有要添加 "-" 号的元素
846+ //
847+ // 设两个集合的元素和分别为 positive 和 negative, 所有元素总和为 sum, 那么有如下等式:
848+ // positive + negative = sum (1)
849+ // positive - negative = target (2)
850+ // (1) 与 (2) 联立可得: positive = (sum + target) / 2,
851+ // 所以如果能从原数组中取出若干个元素形成 1 个元素总和为 (sum + target) / 2 的集合,
852+ // 就算得到了 1 种满足题意的组合方法
853+ //
854+ // 因此, 所求变为: 有多少种取法, 可使得容量为 (sum + target) / 2 的背包被装满?
855+
856+ const sum = nums .reduce ((a , b ) => a + b);
831857
832- if (Math .abs (target) > sum) {
858+ if (Math .abs (target) > sum) {
833859 return 0 ;
834860 }
835861
836- if ((target + sum) % 2 ) {
862+ if ((target + sum) % 2 ) {
837863 return 0 ;
838864 }
839865
840- const halfSum = (target + sum) / 2 ;
841-
842- let dp = new Array (halfSum+ 1 ).fill (0 );
866+ const bagWeight = (target + sum) / 2 ;
867+
868+ // 1. dp 数组的含义
869+ // dp[j]: 装满容量为 j 的背包, 有 dp[j] 种方法
870+ let dp = new Array (bagWeight + 1 ).fill (0 );
871+
872+ // 2. 递推公式
873+ // dp[j] = Σ(dp[j - nums[j]]), (j ∈ [0, j] 且 j >= nums[j])
874+ // 因为 dp[j - nums[j]] 表示: 装满容量为 j - nums[j] 背包有 dp[j - nums[j]] 种方法
875+ // 而容量为 j - nums[j] 的背包只需要再将 nums[j] 放入背包就能使得背包容量达到 j
876+ // 因此, 让背包容量达到 j 有 Σ(dp[j - nums[j]]) 种方法
877+
878+ // 3. dp 数组如何初始化
879+ // dp[0] = 1, dp[1 ~ bagWeight] = 0
843880 dp[0 ] = 1 ;
844-
845- for (let i = 0 ; i < nums .length ; i++ ) {
846- for (let j = halfSum; j >= nums[i]; j-- ) {
881+
882+ // 4. 遍历顺序
883+ // 先物品后背包, 物品从前往后遍历, 背包容量从后往前遍历
884+ for (let i = 0 ; i < nums .length ; i++ ) {
885+ for (let j = bagWeight; j >= nums[i]; j-- ) {
847886 dp[j] += dp[j - nums[i]];
848887 }
849888 }
850889
851- return dp[halfSum ];
890+ return dp[bagWeight ];
852891};
853892```
854893
0 commit comments