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:
- 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.
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.first
,value = 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