322. 零钱兑换

518. 零钱兑换 II

474. 一和零

494. 目标和

思路

1、暴力枚举, DFS
2、动态规划

代码

  1. /**
  2. * @param {number[]} nums
  3. * @param {number} S
  4. * @return {number}
  5. */
  6. var findTargetSumWays = function(nums, S) {
  7. const sum = nums.reduce((total, cur) => total + cur, 0);
  8. const len = nums.length;
  9. let ret = 0;
  10. if (Math.abs(sum) < Math.abs(S)) return ret;
  11. function dfs(idx, sum) {
  12. if (idx === len) {
  13. if (sum === S) {
  14. ret ++
  15. }
  16. return;
  17. } else {
  18. dfs(idx + 1, sum + nums[idx])
  19. dfs(idx + 1, sum - nums[idx])
  20. }
  21. }
  22. dfs(0, 0);
  23. return ret;
  24. };
  1. /**
  2. * @param {number[]} nums
  3. * @param {number} S
  4. * @return {number}
  5. */
  6. var findTargetSumWays = function(nums, S) {
  7. // 计算和
  8. const sum = nums.reduce((total, cur) => total + cur, 0);
  9. // 获取dp数组每一子项的长度为 sum*2+1
  10. const len = nums.length, itemLen = sum * 2 + 1;
  11. let ret = 0;
  12. // 不可能存在
  13. if (Math.abs(sum) < Math.abs(S)) return ret;
  14. const dp = Array(len)
  15. dp[0] = Array(itemLen).fill(0);
  16. // 考虑到第一个数为0的情况,+0,-0 有2种
  17. if (nums[0] === 0) {
  18. dp[0][sum] = 2
  19. } else {
  20. // 将nums[0]用上能够出现的情况
  21. dp[0][sum - nums[0]] = 1
  22. dp[0][sum + nums[0]] = 1
  23. }
  24. for(let i = 1; i < len; i ++) {
  25. let cur = nums[i];
  26. // 初始化其他的dp数组
  27. dp[i] = Array(itemLen).fill(0)
  28. for(let j = 0; j < itemLen; j ++) {
  29. // 边界,如果越界则表示不能满足条件
  30. let left = (j - nums[i] >= 0) ? j - nums[i] : 0;
  31. let right = (j + nums[i] < itemLen) ? j + nums[i] : 0;
  32. dp[i][j] = dp[i-1][left] + dp[i-1][right]
  33. }
  34. }
  35. // 返回目标值
  36. return dp[len-1][S + sum]
  37. };
/**
 * @param {number[]} nums
 * @param {number} S
 * @return {number}
 */
// 因为每一行只和前一行的状态有关系,所以可以缩小空间复杂度
var findTargetSumWays = function(nums, S) {
  // 计算和
  const sum = nums.reduce((total, cur) => total + cur, 0);
  // 获取dp数组每一子项的长度为 sum*2+1
  const len = nums.length, itemLen = sum * 2 + 1;

  let ret = 0;
  // 不可能存在
  if (Math.abs(sum) < Math.abs(S)) return ret;

  const dp = Array(len)
  dp[0] = Array(itemLen).fill(0);
  // 考虑到第一个数为0的情况,+0,-0 有2种
  if (nums[0] === 0) {
    dp[0][sum] = 2
  } else {
    // 将nums[0]用上能够出现的情况
    dp[0][sum - nums[0]] = 1
    dp[0][sum + nums[0]] = 1
  }

  for(let i = 1; i < len; i ++) {
    let cur = nums[i];
    // 创建临时数组用来存储当前行的情况
    let tmp = Array(itemLen).fill(0)
    for(let j = 0; j < itemLen; j ++) {
      // 边界
      let left = (j - nums[i] >= 0) ? j - nums[i] : 0;
      let right = (j + nums[i] < itemLen) ? j + nums[i] : 0;
      tmp[j] = dp[left] + dp[right]
    }
    // 将当前行计算完情况后重新赋值回去
    dp = tmp;
  }
  // 返回
  return dp[S + sum]
};

复杂度分析

时间复杂度 DFS:背包问题 - 图1#card=math&code=O%282%5EN%29) DP: 背包问题 - 图2#card=math&code=O%28N%5E2%29) DP2: 背包问题 - 图3#card=math&code=O%28N%5E2%29)
空间复杂度 DFS: 背包问题 - 图4#card=math&code=O%28N%29) DP: 背包问题 - 图5#card=math&code=O%28N%5E2%29) DP2: 背包问题 - 图6#card=math&code=O%28N%29)

377. 组合总和 Ⅳ