1. 程式人生 > >LeetCode 494. Target Sum (目標和)

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