LeetCode 494. Target Sum (目标和)

原题

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.

Reference Answer

思路分析

DFS超时版:刚开始用的dfs做的,遍历所有的结果,统计满足结果的个数就可以了。没错,超时了。超时的代码如下:

DFS在python中虽然超时,但其回溯思想

return helper(index + 1, acc + nums[index]) + helper(index + 1, acc - nums[index])

十分值得学习!!!
将同样的思想应用到C++中就可以通过,而且简单明了!

Python Code (超时)

class Solution(object):
    def findTargetSumWays(self, nums, S):
        """
        :type nums: List[int]
        :type S: int
        :rtype: int
        """
        def helper(index, acc):
            if index == len(nums):
                if acc == S:
                    return 1
                else:
                    return 0
            return helper(index + 1, acc + nums[index]) + helper(index + 1, acc - nums[index])
        return helper(0, 0)

C++ Code (通过)

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        if (nums.size()==0){
            return 0;
        }
        return helper(nums, 0, 0, S);
    }
    
    int helper(vector<int>& nums, int index, int target, int S){
        if (index == nums.size()){
            if (target == S){
                return 1;
            }
            else{
                return 0;
            }
        }
        return helper(nums, index + 1, target + nums[index], S) + helper(nums, index + 1, target - nums[index], S);   
    }   
};

DP Version:

其实一般能用dfs解决的题目,如果题目只要求满足条件的数字而不是所有的结果,那么dfs会超时。解决方法其实基本只有一条路:动态规划。

设了一个数组,数组中保存的是字典,字典保存的是该index下的能求得的和为某个数的个数。

所以从左到右进行遍历,在每个位置都把前一个位置的字典拿出来,看前一个位置的所有能求得的和。和当前的数值分别进行加减操作,就能得出新一个位置能求得的和了。

要注意一点是,dp初始不能采用下面方式:

dp = [collections.defaultdict(int)] * (_len + 1) 

这种初始化方式会使每个位置的元素其实是同一个字典。

这道题十分值得注意学习,知识点覆盖递归、动态规划、计数(defaultdict)以及index和值运用(如本题,index为求和结果,对应值为得到该值的所有可能)!怎么也没想到dp是用list套dict得到结果。

Python Version (DP)

class Solution:
    def findTargetSumWays(self, nums, S):
        """
        :type nums: List[int]
        :type S: int
        :rtype: int
        """
        
        length = len(nums)
        dp = [collections.defaultdict(int) for _ in range(length+1)]
        dp[0][0] = 1
        for i, num in enumerate(nums):
            for key,value in dp[i].items():
                dp[i+1][key+num] += value
                dp[i+1][key-num] += value
        return dp[length][S]

C++ Version (DP)

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        if (nums.size()==0){
            return 0;
        }
        // return helper(nums, 0, 0, S);
        int length = nums.size();
        vector<unordered_map<int, int>> dp(length+1);
        dp[0][0] = 1;
        for (int i=0; i< length; ++i){
            for(auto &a: dp[i]){
                int sum = a.first, cnt = a.second;
                dp[i+1][sum+nums[i]] += cnt;
                dp[i+1][sum-nums[i]] += cnt;
            }
        }
        return dp[length][S];       
    }

Note

  • 一般能用dfs解决的题目,如果题目只要求满足条件的数字而不是所有的结果,那么dfs会超时。解决方法其实基本只有一条路:动态规划。
  • Python中的dict 对应到 C++ 中就是哈希表 unordered_map<int, int> dp,使用方法近似,但是C++中哈希表遍历一般用 for (auto a : dp) ,元素取用是用key = a.firstvalue = a.second,而本题之所以用for (auto &a : dp[i]),多了引用符号 ‘&’ 的原因是本题在哈希表外套了一个vector,若是只是哈希表,无需多加引用符号。

参考文献

[1] https://blog.csdn.net/fuxuemingzhu/article/details/80484450
[2] http://www.cnblogs.com/grandyang/p/6395843.html

猜你喜欢

转载自blog.csdn.net/Dby_freedom/article/details/84973941