题目概述:
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.
Example 1:
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 = 3 There are 5 ways to assign symbols to make the sum of nums be target 3.
Note:
- The length of the given array is positive and will not exceed 20.
- The sum of elements in the given array will not exceed 1000.
- Your output answer is guaranteed to be fitted in a 32-bit integer.
这道题是一道非常好的考验如何从递归一步步到达dp的题目。拿到这道题一开始肯定的想法都是dfs搜索解空间树,然后就是2^n的时间复杂度。。。emmm,听起来不太舒服对不对,不过肯定是能解的
果不其然,TLE.
下一步就是增加memoization。可以把数组分为两个subset。一个subset里全是+号后面的数字,而另一个则是-号后面的数字。其实只需要求得这两个subset的和并相减,如果等于S,就是一个合理的解。那么dp就可以用来记录下已经遍历过得subset的sum的结果,如果以后再出现,只需要在这个解上+1即可
感觉还是不够舒服对不对?
终结的回答来了,DP! 这道题的dp非常的巧妙,具体可以参考这个回答:
https://blog.csdn.net/mine_song/article/details/70216562
超级巧妙,利用数学推导出一个新的target,只需要对nums求subset sum等于这个new target的subset个数即可。是不是听起来熟悉多了。
python 代码如下:
class Solution(object): def findTargetSumWays(self, nums, S): """ :type nums: List[int] :type S: int :rtype: int """ new_target = (sum(nums) - S) * 0.5 if new_target != int(new_target) or new_target < 0: return 0 dp = [0 for x in range(int(new_target)+1)] dp[0] = 1 for num in nums: for i in range(len(dp)-1, num-1, -1): dp[i] += dp[i-num] return dp[int(new_target)]
这里有几个细节需要注意:
1.这里的dp的意义是:在ith index的位置储存了subset sum为i的subset的个数。注意一定要从右往左更新,这样才保证一个num在一个loop里只能用一次
2. dp[0] = 1表示的是target = sum(nums) 的情况,即将全部数字相加的解,只有一个,为初始情况
3. 注意还要处理一下new_target不合理(没有解)的特殊情况。
但还是感慨一下这个方法的巧妙!
浙公网安备 33010602011771号