Question
You are given a list of non-negative integers, a1, a2, …, an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol.
Find out how many ways to assign symbols to make sum of integers equal to target S.
给你一个包含非负整数的列表以及一个目标S。现在你有两个运算符+和-。对每个整数,你可以选择+或-。找出有多少种方法去使这些整数的总和达到S
Example
Input: nums is [1, 1, 1, 1, 1], S is 3.
Output: 5
Explanation:-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 = 3There are 5 ways to assign symbols to make the sum of nums be target 3.
Solution
动态规划解。因为一部分数字我们赋予+,一部分赋予-,所以我们把赋予+的集合称为P,赋予-的集合称为N。
引自leetcode
sum(P) - sum(N) = target
sum(P) + sum(N) + sum(P) - sum(N) = target + sum(P) + sum(N)
2 sum(P) = target + sum(nums)*所以问题就转化成了如何把使列表的总和达到(target + sum(nums)) / 2(当然要求是target + sum(nums)一定为偶数)。定义dp[i]: 列表总和达到i的方法数。因为这里每个元素只能计算一次,所以每次新加入一个n,更新dp[n:target]。(而不是直接从dp[0]更新到dp[target],这种方法适合每个元素允许多次计算)。递推式: dp[i] += dp[t - i] (i<=t<=target)
class Solution(object): def findTargetSumWays(self, nums, S): """ :type nums: List[int] :type S: int :rtype: int """ # 如果nums所有加起来都达不到S或者 target 不是偶数 (sum(nums) + S) if (sum(nums) + S) % 2 != 0 or S > sum(nums): return 0 # 只要计算nums中的元素总和加到target即可 target = int((sum(nums) + S) / 2) dp = [1] + [0] * target # 因为nums中的每个元素只能使用一次,所以计算每加入一个n,更新dp[n:target] for n in nums: # 一定要从target更新到n,否则会出现重复增加 for t in range(target, n - 1, -1): dp[t] += dp[t - n] # 每出现一个n,都加上总和为t-n时的方法数(因为能够到达t-n,也肯定能到达t) return dp[-1]