leetcode-pp / 91alg-5-daily-check

91 天学算法第五期打卡
55 stars 14 forks source link

【Day 62 】2021-11-10 - 494. 目标和 #81

Open azl397985856 opened 3 years ago

azl397985856 commented 3 years ago

494. 目标和

入选理由

暂无

题目地址

https://leetcode-cn.com/problems/target-sum/

前置知识

返回可以使最终数组和为目标数 target 的所有添加符号的方法数。

示例:

输入:nums: [1, 1, 1, 1, 1], target: 3
输出:5
解释:

-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

一共有5种方法让最终目标和为3。
ginnydyy commented 3 years ago

Problem

https://leetcode.com/problems/target-sum/

Notes

Solution

Complexity

joeytor commented 3 years ago

思路

用 plus_sum, minus_sum 记录加号和减号数字的和, 那么我们可以得到

plus_sum + minus_sum = sum; plus_sum - minus_sum = target

plus_sum = (sum+target) / 2

所以想要找到一个 子数组, 当中的元素和为 (sum+target)/2

dp[i][j] 代表使用前 i 个元素, 能实现 j 的方法数目

base case dp[0][0] = 1

动态转移

dp[i][j] = dp[i-1][j] 使用前 i 个元素实现的方法也可以仅使用前 i-1 个元素实现, 所以就是 前 i-1 个元素实现的方法数

如果 j - nums[i-1] ≥ 0, 那么代表有可能 可以使用 nums[i-1] 和 j-nums[i-1] 来实现数字 j

所以 dp[i][j] += dp[i-nums[i-1]][j-1]

return dp[-1][-1] 返回使用所有元素能实现 目标元素 的方法数目

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        # plus_num + minus_num = sum  &  plus_num - minus_num = target
        # so plus_num = (sum+target) / 2

        t = (sum(nums) + target)
        if t % 2 != 0 or t < 0:
            return 0

        t = int(t/2)
        l = len(nums)

        dp = [[0 for _ in range(t+1)] for _ in range(l+1)]

        dp[0][0] = 1

        for i in range(1,l+1):
            for j in range(0, t+1):
                dp[i][j] = dp[i-1][j]
                if j-nums[i-1] >= 0:
                    dp[i][j] += dp[i-1][j-nums[i-1]]

        return dp[-1][-1]

复杂度

令 n 为元素个数, t 为 (sum+target)/2

时间复杂度: O(nt) 两次遍历的时间复杂度

空间复杂度: O(nt) dp 数组的空间复杂度, 可以优化成一维的 dp 数组

leolisgit commented 3 years ago

题目

https://leetcode.com/problems/target-sum/

思路

这道题有两种方法。一种是通过数学把题目转化为01背包问题。 还有就是通过bottom-up的把数字累加到结果中。因为数字可能全为正或者全为负。 所以加一个offset,值就是sum。dp[i] 表示和为i的所有的排列数。 对每一个数,遍历所有可能的和,范围从-sum到sum,然后取+或者取-,把它加到相应的位置。 这里每次需要申请一个新的数组,因为不然还没用到的上一轮的值会被本轮的结果覆盖。

代码

class Solution {
    public int findTargetSumWays(int[] nums, int target) {

        int sum = 0;
        for (int n : nums) {
            sum += n;
        }
        if (target < -sum || target > sum) return 0;

        int[] dp = new int[2 * sum + 1];

        int offset = sum;

        dp[-nums[0] + offset] += 1;
        dp[nums[0] + offset] += 1;

        for (int i = 1; i < nums.length; i++) {
            int[] next = new int[2 * sum + 1];
            for (int j = -sum; j <= sum; j++) {
                if (dp[j + offset] > 0) {
                    next[j + offset + nums[i]] += dp[j + offset];
                    next[j + offset - nums[i]] += dp[j + offset];
                }
            }
            dp = next;
        }

        return dp[target + sum];
    }
}

复杂度

时间:O(n*sum)
空间:O(n)

linearindep commented 3 years ago

【思路】首先把问题变成一个找子集的问题,也就是那些子集加起来的和可以成为 (sum+target)/2 然后再用背包问题解决 【复杂度】O(n*m) m = (sum+target)/2

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        int ans = 0;

        for(int i =0 ;i < nums.length;i++){
            sum+=nums[i];
        }
        int pos ;
        if(sum<target ||(sum+target)%2==1) return 0;
        pos = (sum + target)/2;
        pos = Math.abs(pos);
        int[] dp = new int[pos+1];
        dp[0] =1;
        for(int i =0; i<nums.length;i++){
            for(int j = pos; j>=nums[i];j--){
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[pos];

    }
}
learning-go123 commented 3 years ago

思路

代码

Go Code:


func findTargetSumWays(nums []int, target int) int {
    sum := 0
    for _, num := range nums {
        sum += num
    }
    // 边界检查
    if sum < target || (target+sum)%2 == 1 || (target+sum)/2 < 0 {
        return 0
    }

    sum = (target + sum) / 2
    dp := make([]int, sum+1)
    dp[0] = 1
    for i := range nums {
        for j := sum; j >= 0; j-- {
            if j-nums[i] >= 0 {
                dp[j] += dp[j-nums[i]]
            }
        }
    }
    return dp[sum]
}

复杂度分析

令 n 为数组长度。

L-SUI commented 3 years ago

var findTargetSumWays = function(nums, target) { let sum = 0; for (const num of nums) { sum += num; } const diff = sum - target; if (diff < 0 || diff % 2 !== 0) { return 0; } const neg = Math.floor(diff / 2); const dp = new Array(neg + 1).fill(0); dp[0] = 1; for (const num of nums) { for (let j = neg; j >= num; j--) { dp[j] += dp[j - num]; } } return dp[neg]; };

muimi commented 3 years ago

思路

题目转换为

user1689 commented 3 years ago

题目

https://leetcode-cn.com/problems/target-sum/

思路

DFS,DP

python3

# 二进制枚举
class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        def dfs(idx, total):
            if (idx, total) in dic:
                return dic[(idx, total)]

            if (idx == n):
                if total == target:
                    return 1
                else:
                    return 0

            left = dfs(idx+1, total + nums[idx])
            right = dfs(idx+1, total - nums[idx])
            dic[(idx, total)] = left + right
            return left + right

        dic = dict()
        n = len(nums)
        return dfs(0, 0)

# 全量DP
class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:

        total = sum(nums)
        if target > total: return 0
        n = len(nums)
        # f[i][j] 代表考虑前 i 个数,当前计算结果为 j 的方案数,令 nums 下标从 1 开始。
        # 那么 f[n][target]为最终答案,f[0][0] = 1 为初始条件:代表不考虑任何数,凑出计算结果为 0 的方案数为 1 种

        dp = [[0 for _ in range(2*total+1)] for _ in range(n + 1)]
        dp[0][0 + total] = 1
        for i in range(1, n+1):
            x = nums[i - 1]
            for j in range(-total, total+1):
                if((j - x) + total >= 0):
                    dp[i][j + total] += dp[i - 1][j - x + total]
                if((j + x) + total <= 2 * total):
                    dp[i][j + total] += dp[i - 1][j + x + total]
        # print(dp)
        return dp[n][target + total]

'''
nums = [1,1,1,1,1]
target = 3
[   
     -5 -4 -3 -2 -1  0  1  2  3  4  5
0    [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], 
1    [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0], 
2    [0, 0, 0, 1, 0, 2, 0, 1, 0, 0, 0], 
3    [0, 0, 1, 0, 3, 0, 3, 0, 1, 0, 0], 
4    [0, 1, 0, 4, 0, 6, 0, 4, 0, 1, 0], 
5    [1, 0, 5, 0, 10, 0, 10, 0, 5, 0, 1]
]
'''

# 优化DP二维
class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        # 状态定义
        # dp[i][j] 代表在数组的前i个数中取元素,使得元素之和为j的方案数
        # 状态转移
        # dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i - 1]] (j >= num)

        # 设neg为负数部分
        # (sum - neg) - neg = target
        # neg = (sum - target) // 2

        # 也可以设pos为正数部分
        # pos - (sum - pos) = target
        # pos = (target + sum) // 2

        # 这里设neg
        sum = 0
        for num in nums: sum += num
        diff = (sum - target)
        if diff < 0 or diff % 2 != 0:
            return 0
        neg = diff // 2
        n = len(nums)
        dp = [[0 for _ in range(neg + 1)] for _ in range(n + 1)]

        # 根据状态定义进行初始化
        # dp[0][0]为从数组前0个元素中选取元素,使得元素之和为0的方案数
        # 当没有元素可取的时候,元素和只能为0,所以方案数为1 即没得选也是一种方案
        dp[0][0] = 1

        for i in range(1, n + 1):
            num = nums[i - 1]
            for j in range(0, neg + 1):
                dp[i][j] = dp[i - 1][j]
                if j >= num:
                    dp[i][j] = dp[i - 1][j] + dp[i - 1][j - num]

        #  nums[1,1,1,1,1], neg = 1
        #   j 0  1
        #   [[1, 0]  0
        #  , [1, 1]  1
        #  , [1, 2]  2
        #  , [1, 3]  3
        #  , [1, 4]  4
        #  , [1, 5]] 5
        #            i

        return dp[n][neg]

# 优化DP一维
class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:

        sum = 0
        for num in nums: sum += num
        diff = (sum - target)
        if diff < 0 or diff % 2 != 0:
            return 0
        neg = diff // 2

        n = len(nums)
        dp = [0 for _ in range(neg + 1)]
        dp[0] = 1
        for i in range(1, n + 1):
            num = nums[i - 1]
            for j in range(neg, -1, -1):
                # dp[j] = dp[j]
                if j >= num:
                    dp[j] = dp[j] + dp[j - num]
                else:
                    break

        return dp[neg]

复杂度分析

相关题目

  1. 01背包
  2. 背包求组合数
  3. https://leetcode-cn.com/problems/expression-add-operators/
RocJeMaintiendrai commented 3 years ago

代码

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        return countTarget(nums, 0, 0, target);
    }

    private int countTarget(int[] nums, int pos, int sum, int target) {
        if(nums.length == pos) return sum == target ? 1 : 0;

        return countTarget(nums, pos + 1, sum - nums[pos], target)
            + countTarget(nums, pos + 1, sum + nums[pos], target);
    }
}

复杂度分析

时间复杂度

O(sum * n)

空间复杂度

O(n)

jiahui-z commented 3 years ago

思路: maintain 2D dp array, store the number of possible ways to reach target

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }

        if (target > sum || target < -sum) return 0;

        int[][] dp = new int[nums.length+1][2*sum+1];
        dp[0][sum] = 1;

        for (int i = 1; i <= nums.length; i++) {
            for (int j = 0; j <= 2*sum; j++) {
                if (j + nums[i-1] <= 2*sum) {
                    dp[i][j] += dp[i-1][j+nums[i-1]];
                }
                if (j - nums[i-1] >= 0) {
                    dp[i][j] += dp[i-1][j-nums[i-1]];
                }
            }
        }

        return dp[nums.length][sum+target];
    }
}

Time Complexity: O(nm), n is the length of nums, m is the sum of all elements Space Complexity: O(nm), n is the length of nums, m is the sum of all elements

chenming-cao commented 3 years ago

解题思路

动态规划。我们将所有取正号的数字的和叫做positive,所有取负号的数字的和叫做negative,所有数组的和sum = positive + negative,我们想要达到的target = positive - negative,此题转化为0-1背包问题,我们找到能够组成和为positive的不同子数组的数目即可。接下来求解positive,结合sum和target的公式,positive = (sum + target) / 2。注意特殊情况的判断,sum + target不能为奇数,同时sum要大于等于target的绝对值(positive >= 0 and negative >= 0)。

这里压缩状态,用一维数组dp[j]表示总和为j的不同子数组的个数,状态转移方程:dp[j] = dp[j] + dp[j - nums[i]]。注意这里要用倒序遍历j,因为我们需要未更新的dp[j - nums[i]]来进行计算。

代码

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }

        if (sum < Math.abs(target)) return 0;
        // sum = positive + negative
        // target = positive - negative
        if ((sum + target) % 2 == 1) return 0;

        int positive = (sum + target) / 2;
        // convert to 0-1 Knapsack problem
        int[] dp = new int[positive + 1];
        dp[0] = 1;

        for (int i = 0; i < nums.length; i++) {
            for (int j = positive; j >= 0; j--) {
                // use reverse order. If we use the normal order, dp[j - nums[i]] has already been updated
                if (j >= nums[i]) dp[j] = dp[j] + dp[j - nums[i]];
            }
        }
        return dp[positive];
    }
}

复杂度分析

CoreJa commented 3 years ago

思路

这个题和昨天的非常像,昨天的我倒是看出来要求和除二分出一半,今天的愣是没看出来

言归正传,显然有如下式子,

        #  pos - neg = target
        #  pos + neg = sum_nums

所以pos=(sum_nums + target)/2

此时,题目转变成了在nums里找若干个数,使其和为pos,问有多少种做法 ( 因为确定了pos后,neg也就确定了 )

这就和昨天的题目非常类似了,不过今天没法用状态压缩处理(因为要存解法)

dp[i][j]表示0到i之间能取任意个数和为j的取法数量

初始化dp[0][0]=1, dp[0][pos-nums[0]]=1 (j>=nums[i])

有如下状态转移关系

发现dp[i]仅与dp[i-1]有关,故可以把dp[i][j]二维数组转换成一维,滚动更新dp。注意此时需要从右到左循环更新(因为j-nums[i]在左侧)

故有如下状态转移关系

代码

    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        sum_nums = sum(nums)
        #  pos - neg = target
        #  pos + neg = sum_nums
        pos = sum_nums + target
        if pos < 0 or pos % 2 == 1:
            return 0
        else:
            pos //= 2
        n = len(nums)
        dp = [1] + [0] * pos
        for i in range(n):
            for j in range(pos, nums[i] - 1, -1):
                dp[j] += dp[j - nums[i]]
        return dp[-1]

复杂度

TC: O(n*(sum-target)) SC: O(sum-target)

falconruo commented 3 years ago

思路: 这道题给了我们一个数组,和一个目标值,让给数组中每个数字加上正号或负号,然后求和要和目标值相等,求有多少种不同的情况

方法一、回溯法 从第一个数字,调用递归函数,在递归函数中,分别对目标值进行加上当前数字调用递归,和减去当前数字调用递归,这样会涵盖所有情况,并且当所有数字遍历完成后,若目标值为0了,则结果 res 自增1

优化: 记忆化递归, 使用数组mem记录中间值,减少重复计算

方法二、动态规划,使用二维数组dp,其中dp[i][j]表示到第i - 1个数字时和为j的情况总数

方法三、动态规划,压缩为一维的哈希表unordered_map<int, int> dp, dp[i] 表示和为i的情况数

复杂度分析:

代码(C++):

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int n = nums.size();

        unordered_map<int, int> dp(n + 1);
        dp[0] = 1;

        for (auto num : nums) {
            unordered_map<int, int> tmp;
            for (auto d : dp) {
                int sum = d.first;
                int cnt = d.second;
                tmp[sum - num] += cnt;
                tmp[sum + num] += cnt;
            }
            dp = tmp;
        }

        return dp[target];
    }
};
simonsayshi commented 3 years ago
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {

        int res = 0;
        helper(nums,S,0,res);
        return res;
    }

    void helper(vector<int>& nums, long S, int indx, int& res) {
        if(indx >= nums.size()) {
            if(S == 0) {
                res++;
            }
            return;
        }
        helper(nums, S - nums[indx], indx + 1, res);
        helper(nums, S + nums[indx], indx + 1 ,res);
    }
};
banjingking commented 3 years ago

思路

Top Down Dynamic Programming

代码

class Solution {
public boolean canPartition(int[] nums) {
    int totalSum = 0;
    // find sum of all array elements
    for (int num : nums) {
        totalSum += num;
    }
    // if totalSum is odd, it cannot be partitioned into equal sum subset
    if (totalSum % 2 != 0) return false;
    int subSetSum = totalSum / 2;
    int n = nums.length;
    Boolean[][] memo = new Boolean[n + 1][subSetSum + 1];
    return dfs(nums, n - 1, subSetSum, memo);
}

public boolean dfs(int[] nums, int n, int subSetSum, Boolean[][] memo) {
    // Base Cases
    if (subSetSum == 0)
        return true;
    if (n == 0 || subSetSum < 0)
        return false;
    // check if subSetSum for given n is already computed and stored in memo
    if (memo[n][subSetSum] != null)
        return memo[n][subSetSum];
    boolean result = dfs(nums, n - 1, subSetSum - nums[n - 1], memo) ||
            dfs(nums, n - 1, subSetSum, memo);
    // store the result in memo
    memo[n][subSetSum] = result;
    return result;
}

}

Time complexity

空间复杂度: O(mn)

时间复杂度: O(mn)

kennyxcao commented 3 years ago

494. Target Sum

Intuition

Code

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
const findTargetSumWays = function(nums, target) {
  const n = nums.length;
  const maxSum = nums.reduce((acc, num) => acc + num, 0);
  if (maxSum < Math.abs(target)) return 0;
  const range = maxSum << 1; // range is [-maxSum...maxSum]
  // dp[i][j] = num of ways to reach sum j (offset by maxSum) with first i nums
  const dp = Array.from({length: n}, () => Array(range + 1).fill(0));
  dp[0][maxSum + nums[0]] += 1;
  dp[0][maxSum - nums[0]] += 1;
  for (let i = 1; i < n; i++) {
    for (let sum = -maxSum; sum <= maxSum; sum++) {
      const offset = sum + maxSum; // offset by maxSum
      if (dp[i - 1][offset] > 0) {
        dp[i][offset + nums[i]] += dp[i - 1][offset];
        dp[i][offset - nums[i]] += dp[i - 1][offset];
      }
    }
  }
  return dp[n - 1][target + maxSum];
};

Complexity Analysis

15691894985 commented 3 years ago

day 61 494. 目标和

https://leetcode-cn.com/problems/target-sum/

add1 -neg1 = target #分别代表正数的和,负数的和
add1 + neg1 = sumnums
add1 = (target + summnums)/2  问题转变为在nums里找若干个数,使得其和为add1
#状态:dp[i][j]0-i的数字组合得到j的数目 d[0][0] = 1 dp[0][add1-nums[0]]=1 (add1 >=num[i])
状态转移:dp[i][j]= dp[i-1][j] + (if j > nums[i]  dp[i-1][j-nums[i]])
第i行只依赖于i-1行  进一步进行状态压缩
class Solution:
 def findTargetSumWays(self, nums: List[int], target: int) -> int:
        t = sum(nums) + target
        if t % 2 or t < 0:
            return 0
        t= t // 2
        dp = [0] * (t+1)
        dp[0] = 1
        for n in nums:
            for j in range(t,n-1,-1):
                dp[j] += dp[j-n]
        return dp[-1]

复杂度分析:

时间复杂度:(n*t)

空间复杂度:O( t+1)

Laurence-try commented 3 years ago

思路

DP

代码

使用语言:Python3

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
#         # Brute Force
#         self.count = 0
#         self.helper(nums, 0, 0, target)
#         return self.count

#     def helper(self, nums, i, curr_sum, t):
#         if i == len(nums):
#             if curr_sum == t:
#                 self.count += 1
#         else:
#             self.helper(nums, i+1, curr_sum + nums[i], t)
#             self.helper(nums, i+1, curr_sum - nums[i], t)

        # DP
        total = sum(nums)
        n_total = -total
        if target > total:
            return 0
        dp = [[0] * (2 * total + 1) for _ in range(len(nums))]
        dp[0][total + nums[0]] = 1
        dp[0][total - nums[0]] += 1

        for i in range(1, len(nums)):
            for j in range(n_total, total + 1):
                if dp[i - 1][j + total] > 0:
                    dp[i][j + nums[i] + total] += dp[i - 1][j + total]
                    dp[i][j - nums[i] + total] += dp[i - 1][j + total]
        return dp[len(nums) - 1][target + total]

复杂度分析 时间复杂度:O(mn) 空间复杂度:O(mn)

chen445 commented 3 years ago

代码

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        memo={}
        def dfs(start_index,current_target):
            if start_index == len(nums)-1:
                if nums[start_index] ==0 and current_target==0:
                    return 2
                elif current_target== nums[start_index] or current_target==-nums[start_index]:
                    return 1
                else:
                    return 0
            total_expressions=0
            next_target=current_target-nums[start_index]

            if (start_index+1,next_target) in memo:
                total_expressions+=memo[(start_index+1,next_target)]
            else:
                total_expressions+=dfs(start_index+1,next_target)

            next_target=current_target+nums[start_index]
            if (start_index+1,next_target) in memo:
                total_expressions+=memo[(start_index+1,next_target)]
            else:
                total_expressions+=dfs(start_index+1,next_target)
            memo[(start_index,current_target)]=total_expressions
            return total_expressions
        return dfs(0,target)

复杂度

Time: O(n*t) t is the sum of target, n is the length of nums

Space: O(n*t)

HouHao1998 commented 3 years ago

代码

    public static int process1(int[] arr, int index, int rest) {
        if(index == arr.length){
            return rest==0?1:0;
        }
        return  process1(arr,index+1,rest+arr[index])+process1(arr,index+1,rest-arr[index]);
    }

复杂度

watermelonDrip commented 3 years ago
class Solution:
    def findTargetSumWays(self, nums: List[int], S: int) -> int:
        # dp[][]
        n = len(nums)
        numS = sum(nums)
        if numS == S and len(nums)== 1:
            return 1 
        if numS == -S and len(nums)== 1:
            return 1 
        if numS<S:
            return 0

        dp = [ [0 for _ in range(2*numS+1)] for _ in range(n) ]
        dp[0][numS+nums[0]] = 1
        dp[0][numS-nums[0]] += 1
        for i in range(1,n):
            for j in range(-numS -1, numS +1):
                l = dp[i-1][ numS+j-nums[i]] if 0 <=  numS+ j - nums[i] < 2* numS +1 else 0
                r = dp[i-1][ numS+j+nums[i]] if 0 <= numS+ j + nums[i] <  2*numS +1 else 0
                dp[i][numS+j] = l + r

        return dp[-1][numS+S]
chaggle commented 3 years ago

title: "Day 62 494. 目标和" date: 2021-11-10T16:57:32+08:00 tags: ["Leetcode", "c++", "NP"] categories: ["91-day-algorithm"] draft: true


494. 目标和

题目

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

 

示例 1:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:

输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。
 

提示:

1 <= nums.length <= 200
1 <= nums[i] <= 100

题目思路

  • 1、np背包问题查看大佬的模板解法,仅需要进行一些改动即可解题,本质上属于同一类的题目
  • 2、两种边界条件的情况可以直接排除,数组和小于目标值以及数组差值为奇数。
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = accumulate(nums.begin(), nums.end(), 0);
        if((sum - target) % 2 == 1 || sum < target) return false;

        int n = (sum - target) / 2;
        vector<int> dp(n + 1, 0);
        dp[0] = 1; 
        for(int i : nums) 
        {
            for (int j = n; j >= i; j--) {
                dp[j] += dp[j - i]; 
            }
        }
        return dp[n];
    }
};

复杂度

参考文章

https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/yi-pian-wen-zhang-chi-tou-bei-bao-wen-ti-a7dd/

asterqian commented 3 years ago

思路

将题目转换为是否能找到子序列使得其和为array的总和/2,成为0-1背包问题

代码

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for (int x: nums) sum += x;
        if (abs(target) > sum) return 0;
        if (nums.size() == 1) {
            return nums[0] == abs(target) ? 1 : 0;
        }
        if ((target + sum) % 2 == 1) return 0;
        int positive = (target + sum) / 2;
        vector<int> dp(positive + 1);
        dp[0] = 1;
        for (int i = 0; i < nums.size(); ++i) {
            for (int j = positive; j >= nums[i]; --j) {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp.back();
    }
};

Time Complexity: O(n * (total + target) / 2)

Space Complexity: O((total + target) / 2)

ABOUTY commented 3 years ago
class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        if not nums:
            return 0
        dic = defaultdict(int)
        if nums[0] != 0:     
            dic[nums[0]] = 1
            dic[-nums[0]] = 1
        else:
            dic[nums[0]] = 2

        for i in range(1, len(nums)):
            tdic = defaultdict(int)
            for d in dic:

                tdic[d + nums[i]] = tdic[d + nums[i]] + dic[d]
                tdic[d - nums[i]] = tdic[d - nums[i]] + dic[d]
            dic = tdic
        return dic[target]
LareinaWei commented 3 years ago

Thinking

DP. 赶due, 先打卡回来再补!

Code

class Solution:
    def findTargetSumWays(self, nums, target) -> bool:
        t = sum(nums) + target
        if t % 2:
            return 0
        t = t // 2

        dp = [0] * (t + 1)
        dp[0] = 1

        for i in range(len(nums)):
            for j in range(t, nums[i] - 1, -1):
                dp[j] += dp[j - nums[i]]
        return dp[-1]
wenlong201807 commented 3 years ago

代码块


const findTargetSumWays = (nums, target) => {
  const sum = nums.reduce((a, b) => a + b);

  if (target > sum) {
    return 0;
  }

  if ((target + sum) % 2) {
    return 0;
  }

  const halfSum = (target + sum) / 2;
  nums.sort((a, b) => a - b);

  let dp = new Array(halfSum + 1).fill(0);
  dp[0] = 1;

  for (let i = 0; i < nums.length; i++) {
    for (let j = halfSum; j >= nums[i]; j--) {
      dp[j] += dp[j - nums[i]];
    }
  }

  return dp[halfSum];
};

时间复杂度和空间复杂度

RonghuanYou commented 3 years ago
class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        dp = [defaultdict(int) for _ in range(len(nums) + 1)]

        dp[0][0] = 1

        for i, num in enumerate(nums):
            for s, cnt in dp[i].items():
                dp[i + 1][s + num] += cnt
                dp[i + 1][s - num] += cnt
        return dp[len(nums)][target]
biscuit279 commented 3 years ago

思路:转换成01背包问题

class Solution(object):
    def findTargetSumWays(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        t = sum(nums) + target
        if t%2 != 0:
            return 0
        t = abs(t//2)
        dp = [0] * (t+1)
        dp[0] = 1
        for i in range(len(nums)):
            for j in range(t, nums[i]-1, -1):
                dp[j] += dp[j-nums[i]]
        return dp[-1]

时间复杂度:O(n*(target+total)/2) 空间复杂度:O((target+total)/2)

GZ712D commented 3 years ago

思路 DP

代码 使用语言:Python3

class Solution: def findTargetSumWays(self, nums: List[int], target: int) -> int:

    # DP
    total = sum(nums)
    n_total = -total
    if target > total:
        return 0
    dp = [[0] * (2 * total + 1) for _ in range(len(nums))]
    dp[0][total + nums[0]] = 1
    dp[0][total - nums[0]] += 1

    for i in range(1, len(nums)):
        for j in range(n_total, total + 1):
            if dp[i - 1][j + total] > 0:
                dp[i][j + nums[i] + total] += dp[i - 1][j + total]
                dp[i][j - nums[i] + total] += dp[i - 1][j + total]
    return dp[len(nums) - 1][target + total]

复杂度分析 时间复杂度:O(mn) 空间复杂度:O(mn)

hellowxwworld commented 3 years ago

思路

动态规划,先操别人答案;

    int findTargetSumWays(vector<int>& nums, int target) {
        nums.insert(nums.begin(), 0);
        const int N = nums.size();
        int sum = 0;
        for (int& x : nums)
            sum += x; /* 题意: sum(nums[i]) <= 1000, 后面需要用作+offset确保dp数组的index >= 0 */
        if (target > sum || target < -sum)
            return 0;
        auto dp = vector<vector<int>>(N, vector<int>(2 * sum + 1, 0));
        dp[0][0 + sum] = 1; /* dp[i][S]: 用前i个数进行计算后得到和为S的方法的数量.
                               dp数组的第2维统一 +sum确保这一维的index >= 0 
                            */
        for (int i = 1; i < N; i++) {
            for (int s = -sum; s <= sum; s++) {
                if (s + nums[i] + sum <= 2 * sum)
                    dp[i][s + nums[i] + sum] += dp[i - 1][s + sum];
                if (s - nums[i] + sum >= 0)
                    dp[i][s - nums[i] + sum] += dp[i - 1][s + sum];
            }
        }
        return dp[N - 1][target + sum];
    }
ZJP1483469269 commented 3 years ago
class Solution {
    public int findTargetSumWays(int[] nums, int target){
        int len = nums.length;
        int sum = 0;
        for(int x : nums){
            sum += x;
        }
        if(target > sum || target < -sum) return 0;
        int[][] dp = new int[len+1][(sum << 2) + 1];
        dp[0][sum] = 1;
        for(int i = 1; i <= len;i++){
            for(int j = 0 ; j <= sum << 2; j++){
                if(dp[i-1][j] != 0){
                    dp[i][j - nums[i-1]] += dp[i - 1][j];                    
                    dp[i][j + nums[i-1]] += dp[i - 1][j];
                }
            }
        }
        return dp[len][sum + target];
    }
}
KennethAlgol commented 3 years ago

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int i: nums) sum += i;
        if (Math.abs(target) > sum) return 0;
        int[][] dp = new int[nums.length][sum * 2 + 1];

        dp[0][nums[0] + sum] = 1;
        dp[0][sum - nums[0]] += 1;
        for (int i = 1; i < nums.length; i++) {
            for (int j = 0; j <= 2 * sum; j++) {
                if (j - nums[i] < 0) {
                    dp[i][j] = dp[i-1][j+nums[i]];
                } else if (j + nums[i] > 2 * sum) {
                    dp[i][j] = dp[i-1][j-nums[i]];
                } else {
                    dp[i][j] = dp[i-1][j+nums[i]] + dp[i-1][j-nums[i]];
                }

            }
        }
        return dp[nums.length - 1][target + sum];
    }
}
carinSkyrim commented 3 years ago

代码

class Solution:
    def findTargetSumWays(self, nums, target) -> bool:
        t = sum(nums) + target
        if t % 2:
            return 0
        t = t // 2

        dp = [0] * (t + 1)
        dp[0] = 1

        for i in range(len(nums)):
            for j in range(t, nums[i] - 1, -1):
                dp[j] += dp[j - nums[i]]
        return dp[-1]
carterrr commented 3 years ago
class 目标和_494 {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0 , length = nums.length;

        for(int i = 0; i < length; i ++) {
            sum += nums[i];
        }
        //  正数和为n  则 n -( sum -n) = target  => n = (sum + target) / 2  转化为求正数n  使得所有加上+号的数字 和为 (sum + target) / 2
        if( sum < target || (sum + target) % 2 == 1) return 0;
        target = Math.abs((sum + target)/ 2); //如果target为负值  转化为求正值  结果等价  那为什么不直接取加上-号的数字的和呢???? ( sum -n) - n = target  => n = (sum -target)/ 2 后续重做用这个

        int[][] dp  = new int[length + 1][target + 1]; // 取+的数字个数可能为 0 ~ nums.length    ,  和数值取值范围为  0 ~ target  
        dp[0][0] = 1; // 1个都不取 和为 0 的种类数为1  其他dp[0][i] = 0 ; dp[i][0]在递推过程中求得
        for(int i = 1; i<= length; i++) {
            for(int j =0; j<= target; j ++ ) {
                int x = nums[i - 1];
                // 求和为j  每个数字有取或者不取两种
                if(x <= j) dp[i][j] += dp[i - 1][j - x]; // 取nums[i]
                dp[i][j] += dp[i - 1][j];// 不取nums[i]

               // 如果简化为1维滚动  可以改成 倒序   if(x <= j) dp[j] += dp[j -x]  
            }
        }

        return dp[length][target];
    }
}
cecilialmw commented 3 years ago
class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }

        if ((target > sum) || ((target + sum) % 2 == 1)) {
            return 0;
        }

        int positive = (target + sum) / 2;

        int[] dp = new int[positive + 1];
        dp[0] = 1;
        for (int i = 0; i < nums.length; i++) {
            for (int j = positive; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }

        return dp[positive];
    }
}

Complexity:

Time: O(n (sum + target)/2) = O(n (sum + target))

Space: O((sum + target) / 2) = O(sum + target)

nonevsnull commented 3 years ago

思路

代码

public int findTargetSumWays(int[] nums, int target) {

    int sum = 0;
    for (int num : nums)
        sum += num;

    if (sum < Math.abs(target))
        return 0;

    if (((sum + target) & 1) == 1)
        return 0;

    sum = (sum + target) / 2;
    int[] dp = new int[sum + 1];
    dp[0] = 1;

    for (int i = 0; i < nums.length; i++)
        for (int j = sum; j >= nums[i]; j--)
            dp[j] = dp[j] + dp[j - nums[i]];

    return dp[sum];
}

复杂度

time: O(negative * (total + target) / 2) space: O((total + target) / 2)

wangyifan2018 commented 3 years ago
class Solution:
    def solve(self, nums, target):
        if (sum(nums) + target) % 2 == 1: return 0
        t = (sum(nums) + target) // 2
        dp = [[0] * (len(nums) + 1) for _ in range(t + 1)]
        dp[0][0] = 1
        for i in range(t + 1):
            for j in range(1, len(nums) + 1):
                dp[i][j] = dp[i][j-1]
                if i - nums[j-1] >= 0: dp[i][j] += dp[i - nums[j-1]][j-1]
        return dp[-1][-1]
jaysonss commented 3 years ago

思路

转化成01背包问题,不过不是求最大价值,而是求重量为M有几种方式

状态转换方程为dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]]

此外,还有一些边界情况:

sum<abs(target),则结果不存在

sum+target为奇数,会导致最终和不是target,返回0

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum =0;

        for(int num: nums){
            sum+=num;
        }
        if(sum < Math.abs(target))return 0;
        if(((sum + target) & 1)==1)return 0;
        sum = (sum + target)/2;

        ///dp[i]只能通过dp[i-1]获得,所以可以压缩为1维,并且遍历j时要倒序
        int[] dp = new int[sum+1];
        dp[0]=1;
        for(int i=0;i<nums.length;i++){
            for(int j=sum;j>=nums[i];j--){
                dp[j] = dp[j] + dp[j-nums[i]];
            }
        }
        return dp[sum];
    }
}
Richard-LYF commented 3 years ago

class Solution: def findTargetSumWays(self, nums: List[int], target: int) -> int: t = sum(nums) + target if t % 2: return 0 t = t // 2

    dp = [0] * (t + 1)
    dp[0] = 1

    for i in range(len(nums)):
        for j in range(t, nums[i] - 1, -1):
            dp[j] += dp[j - nums[i]]
    return dp[-1]
kendj-staff commented 3 years ago
class Solution {
    public int findTargetSumWays(int[] nums, int S) {
        int sum = 0;
        for(int num : nums) sum += num;
        if(S > sum || (S + sum) % 2 == 1) return 0;
        int target = (S + sum) / 2;
        int[] dp = new int[target + 1];
        dp[0] = 1;
        for(int num : nums){
            for(int j = target; j >= num; j--){
                dp[j] = dp[j] + dp[j - num];
            }
        }
        return dp[target];
    }
}
newbeenoob commented 3 years ago

思路


转化为 01 背包问题,转化过程见下代码注释:

代码:JavaScript

var findTargetSumWays = function(nums, target) {
    // pos : nums的全正数子集 nag : nums 的全负数子集 其中有 pos + -nag = sum (nums正数和)

    // pos + nag === target

    // pos + posCounterpart + nag = target + posCounterPart

    // obviously : pos + posCounterpart = sum

    // sum + nag === target + posCounterPart

    // we have : -nag = posCounterPart

    // sum === target + 2posCounterPart

    // posCounterPart = (sum - target) / 2

    // 所以题目转换为 寻找等于 (sum - target) / 2 的 posCounterPart

    const sum = nums.reduce((acc ,x) => acc + x , 0);

    if( ((sum -  target) & 1) === 1 || sum - target < 0) return 0;

    let ans = 0;

    const w = (sum - target) >> 1;

    const dp = new Array(w + 1).fill(0);

    dp[0] = 1;

    for(let i = 0 ; i < nums.length ; ++i) {
        for(let j = w ; j >= nums[i] ; --j){
            dp[j] += dp[j - nums[i]];
        }
    }

    return dp[w];

};

时间复杂度: o(n*(sum - target)) 额外空间复杂度: o(sum - target)

附带 01 背包和完全背包的模板:

//01背包
for (let i = 0; i < n; i++) {
    for (let j = m; j >= V[i]; j--) {
        f[j] = Math.max(f[j], f[j-V[i]] + W[i]); // max / min / count
    }
}

//完全背包
for (let i = 0; i < n; i++) {
    for (let j = V[i]; j <= m; j++) {
        f[j] = Math.max(f[j], f[j-V[i]] + W[i]); // max / min / count
    }
}
guangsizhongbin commented 3 years ago
class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int num : nums){
            sum += num;
        }

        int differ = sum - target;
        // 如果target > sum 或 differ没法对半分
        if  (differ < 0 || differ % 2 != 0){
            return 0;
        }

        int neg = differ / 2;
        int[] dp = new int[neg + 1];
        dp[0] = 1;
        for (int num: nums){
            for (int j = neg; j >= num; j--){
                dp[j] += dp[j - num];
            }
        }
        return dp[neg];
    }
}
Jinjin680 commented 3 years ago

思路

代码

class Solution {
public:
  int findTargetSumWays(vector<int>& nums, int S) {
    const int sum = std::accumulate(nums.begin(), nums.end(), 0);
    if (sum < std::abs(S)) return 0;
    int ans = 0;
    dfs(nums, 0, S, ans);
    return ans;
  }
private:
  void dfs(const vector<int>& nums, int d, int S, int& ans) {
    if (d == nums.size()) {
      if (S == 0) ++ans;
      return;
    }    
    dfs(nums, d + 1, S - nums[d], ans);
    dfs(nums, d + 1, S + nums[d], ans);
  }
};

复杂度分析

cy-sues commented 3 years ago
class Solution {
    int count = 0;

    public int findTargetSumWays(int[] nums, int target) {
        backtrack(nums, target, 0, 0);
        return count;
    }

    public void backtrack(int[] nums, int target, int index, int sum) {
        if (index == nums.length) {
            if (sum == target) {
                count++;
            }
        } else {
            backtrack(nums, target, index + 1, sum + nums[index]);
            backtrack(nums, target, index + 1, sum - nums[index]);
        }
    }
}
ymkymk commented 3 years ago

思路

动态规划

代码

``


class Solution {

       private static final int TWO = 2;

    /**
     * 如果我们把 nums 划分成两个子集 A 和 B,分别代表分配 + 的数和分配 - 的数,那么他们和 target 存在如下关系:
     * sum(A) - sum(B) = target
     * sum(A) = target + sum(B)
     * sum(A) + sum(A) = target + sum(B) + sum(A)
     * 2 * sum(A) = target + sum(nums)
     * 综上,可以推出 sum(A) = (target + sum(nums)) / 2,也就是把原问题转化成:nums 中存在几个子集 A,使得 A 中元素的和为 (target + sum(nums)) / 2?
     *
     */
    int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int n : nums) {
            sum += n;
        }
        // 这两种情况,不可能存在合法的子集划分
        if (sum < target || (sum + target) % TWO == 1 || (sum + target) < 0) {
            return 0;
        }
        return subsets(nums, (sum + target) / TWO);
    }

    /**
     * 计算 nums 中有几个子集的和为 sum
     * dp[i][j] = x 表示,若只在前 i 个物品中选择,若当前背包的容量为 j,则最多有 x 种方法可以恰好装满背包
     *
     * @param nums nums数组
     * @param sum 和
     * @return 个数
     */
    int subsets(int[] nums, int sum) {
        int n = nums.length;
        int[][] dp = new int[n + 1][sum + 1];
        // base case
        for (int i = 0; i <= n; i++) {
            dp[i][0] = 1;
        }

        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= sum; j++) {
                if (j >= nums[i-1]) {
                    // 两种选择的结果之和,第一个不装包,第二个装包
                    dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i-1]];
                } else {
                    // 背包的空间不足,只能选择不装物品 i
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        return dp[n][sum];
    }
}

复杂度分析

时间复杂度:O(n * sum)

空间复杂度:O(n * sum)

mokrs commented 3 years ago
int findTargetSumWays(vector<int>& nums, int target) {
    int sum = 0;
    for (int i = 0; i < nums.size(); ++i) {
        sum += nums[i];
    }

    if (abs(target) > sum) {
        return 0;
    }

    if ((target + sum) % 2 == 1) {
        return 0;
    }

    int size = (target + sum) / 2;
    vector<int> dp(size + 1, 0);
    dp[0] = 1;
    for (int i = 0; i < nums.size(); i++) {
        for (int j = size; j >= nums[i]; j--) {
            dp[j] += dp[j - nums[i]];
        }
    }
    return dp[size];
}
WeiWri-CC commented 3 years ago
class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:

        #dp[i][j] num of ways sum to J using 0:i num
        if (sum(nums)+target) %2 ==1:
            return 0

        t = (sum(nums)+target) //2
        if t <0:
            return 0
        dp = [[0 for _ in range(len(nums)+1)] for _ in range(t+1)]
        print(dp)
        dp[0][0] = 1
        for i in range(t+1):
            for j in range(1,len(nums)+1):
                dp[i][j] = dp[i][j-1]
                if i >= nums[j-1]:
                    dp[i][j] += dp[i-nums[j-1]][j-1]
        return dp[-1][-1]
Mrhero-web commented 3 years ago

int findTargetSumWays(vector& nums, int target) { int sum = 0; for (int i = 0; i < nums.size(); ++i) { sum += nums[i]; }

if (abs(target) > sum) {
    return 0;
}

if ((target + sum) % 2 == 1) {
    return 0;
}

int size = (target + sum) / 2;
vector<int> dp(size + 1, 0);
dp[0] = 1;
for (int i = 0; i < nums.size(); i++) {
    for (int j = size; j >= nums[i]; j--) {
        dp[j] += dp[j - nums[i]];
    }
}
return dp[size];

}

falsity commented 3 years ago

思路:

定义状态 搞清楚需要输出的结果后,就可以来想办法画一个表格,也就是定义dp数组的含义。根据背包问题的经验,可以将dp[ i ][ j ]定义为从数组nums中 0 - i 的元素进行加减可以得到 j 的方法数量。

状态转移方程 搞清楚状态以后,我们就可以根据状态去考虑如何根据子问题的转移从而得到整体的解。这道题的关键不是nums[i]的选与不选,而是nums[i]是加还是减,那么我们就可以将方程定义为:

dp[ i ] [ j ] = dp[ i - 1 ] [ j - nums[ i ] ] + dp[ i - 1 ] [ j + nums[ i ] ] 可以理解为nums[i]这个元素我可以执行加,还可以执行减,那么我dp[i] [j]的结果值就是加/减之后对应位置的和。

参考链接:

https://leetcode-cn.com/problems/target-sum/solution/dong-tai-gui-hua-si-kao-quan-guo-cheng-by-keepal/

class Solution {
        public int findTargetSumWays(int[] nums, int target) {
            int n = nums.length;
            int sum = 0;
            for (int num : nums) {
                sum += num;
            }
            //边界条件
            if(target < -sum || target > sum){
                return 0;
            }
            int t = 2 * sum + 1;
            //分步达到结果 动态规划
            int[][] dp = new int[n][t];
            //初始化
            if (nums[0] == 0) {
                dp[0][sum] = 2; //索引只能从0开始,所以[-sum,sum]变成[0,2*sum] 0 -> sum
            } else {
                dp[0][sum + nums[0]] = 1;
                dp[0][sum - nums[0]] = 1;
            }
            for (int i = 1; i < nums.length; i++) {
                for (int j = 0; j < t; j++) {
                    //判断dp[i - 1][j - nums[i]]是否存在
                    if (j - nums[i] >= 0) {
                        dp[i][j] += dp[i - 1][j - nums[i]];
                    }
                    //判断dp[i - 1][j + nums[i]]是否存在
                    if (j + nums[i] < t) {
                        dp[i][j] += dp[i - 1][j + nums[i]];
                    }
                }
            }

            return dp[n - 1][sum + target];

        }
    }

时间复杂度:O(N*SUM)

空间复杂度:O(N*SUM)

laurallalala commented 3 years ago
class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:

        #dp[i][j] num of ways sum to J using 0:i num
        if (sum(nums)+target) %2 ==1:
            return 0

        t = (sum(nums)+target) //2
        if t <0:
            return 0
        dp = [[0 for _ in range(len(nums)+1)] for _ in range(t+1)]
        print(dp)
        dp[0][0] = 1
        for i in range(t+1):
            for j in range(1,len(nums)+1):
                dp[i][j] = dp[i][j-1]
                if i >= nums[j-1]:
                    dp[i][j] += dp[i-nums[j-1]][j-1]
        return dp[-1][-1]