leetcode-494 目标和

1. 题目

给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

2. 解题思路

  1. 回溯法
    数组中每个值都有两种选择,乘1或者乘-1,通过递归的方式回溯,遍历每一种情况,如果符合条件将结果加1
    时间复杂度太高,会超时
  2. 动态规划
    假设所有取正的数字之和为x,所有取负的数字之和为y,则
    \(target=x-y\)
    \(sum=x+y\)
    \(x=\frac{sum+target}{2}\)
    此时问题变为01背包问题,背包容量为\(\frac{sum+target}{2}\),即在背包中取出该大小的东西的组合数
    令dp[j]为 填满j容量的背包,有dp[j]种方法
    1. 初始化:dp[0]=1,即填满容量为0的背包有一种组合方式
    2. 状态转移方程:dp[i] = dp[i] + dp[i - num]
      当前填满容量为j的包的方法数 = 之前填满容量为j的包的方法数 + 之前填满容量为j - num的包的方法数,也就是当前数num的加入,可以把之前和为j - num的方法数加入进来。

3. 代码

  1. 回溯法
class Solution:
    def __init__(self):
        self.res = 0
        self.op = [1, -1]

    def findTargetSumWays(self, nums, target: int) -> int:
        self.back(nums, target, 0, 0)
        return self.res

    def back(self, nums, target, index, total):
        if index == len(nums) and total == target:
            self.res += 1
        else:
            if index != len(nums):
                for i in self.op:
                    total += i * nums[index]
                    self.back(nums, target, index+1, total)
                    total -= i * nums[index]
  1. 动态规划
class Solution:
    def findTargetSumWays(self, nums, target: int) -> int:
        # target=x-y
        # sum=x+y
        # x=(sum+target)/2
        x = (sum(nums) + target) // 2
        if target > sum(nums) or (target + sum(nums)) % 2:
            return 0
        dp = [0] * (x + 1)
        # 填满容量为j的背包,有dp[j]种方法
        dp[0] = 1
        for num in nums:
            for i in range(x, num - 1, -1):
                # 当前填满容量为j的包的方法数 = 之前填满容量为j的包的方法数 + 之前填满容量为j - num的包的方法数也就是当前数num的加入,可以把之前和为j - num的方法数加入进来。
                dp[i] = dp[i] + dp[i - num]

        return dp[-1]