原题
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