导航

LeetCode 494. Target Sum

Posted on 2019-01-30 11:44  隔岸小杨  阅读(94)  评论(0)    收藏  举报

题目概述:

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:

  1. The length of the given array is positive and will not exceed 20.
  2. The sum of elements in the given array will not exceed 1000.
  3. 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不合理(没有解)的特殊情况。

但还是感慨一下这个方法的巧妙!