494.目标和

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

一:暴力遍历(回溯)

py超时,但java没有

从0开始递归数组,直到n位置终止,每次分别回溯尝试+和-

class Solution:
    def findTargetSumWays(self, nums, target: int) -> int:
        self.res = 0	#全局计数
        def bt(start, sum):
            if start  == len(nums):	#递归出口
                if sum == target:
                    self.res += 1	#满足条件,结果加一
                return
            #注意不要直接修改sum,即“保存现场”
            bt(start+1, sum+nums[start])	#加号
            bt(start+1, sum-nums[start])	#减号
        #开始
        bt(0, 0)
        return self.res

二:dp

记数组的元素和为sum,添加-号的元素之和为neg,则其余添加 + 的元素之和为sum-neg。由题意,有:

(sum−neg)−neg = sum−2*neg = target

neg = (sum−target)/2

题目中nums元素非负,因此neg也非负,当sum−target为负直接返回0

问题转化成在数组 nums 中选取若干元素,使得这些元素之和等于 neg,计算选取元素的方案数。是一个01背包问题,可以用dp

与416. 分割等和子集类似,设dp[i][j] 表示在数组nums 的前 i个数中选取元素,使得这些元素之和等于 j 的方案数,i=0表示不取,因此i=1表示数组0号元素,即dp[i][j]表示能否用数组[0...i-1]凑出,dp方程为

dp[i][j] =  dp[i-1][j]    +    dp[i-1][j-nums[i-1]]
              不用i-1                   用i-1

base case:

dp[0][j] = 0	#0个数什么也凑不出
dp[i][0] = 1	#凑出0有一种方案

最终所求为dp[n][neg](n为nums长度)

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        s = sum(nums)
        diff = s - target
        if diff < 0 or diff % 2 != 0:   #无法构造
            return 0
        neg = diff // 2
        n = len(nums)
        dp = [[0] * (neg+1) for i in range(n+1)]
        for i in range(n+1):	#关键,base case
            dp[i][0] = 1
        for i in range(1, n+1):
            for j in range(neg+1):
                if j-nums[i-1] >= 0:
                    dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i-1]]
                else:
                    dp[i][j] = dp[i-1][j]
        return dp[n][neg]

最后更新于