https://leetcode-cn.com/problems/target-sum/
思路一:爆搜出奇迹。
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
return dfs(nums,0,target);
}
int dfs(const vector<int>&nums, int idx,int target)
{
if(idx==nums.size())
return !target;
return dfs(nums,idx+1,target+nums[idx])+dfs(nums,idx+1,target-nums[idx]);
}
};
思路二:dp,先要对题目做一个转换。考虑数组元素之和为
s
u
m
sum
sum,假设挑选若干个元素使得它们前面的符号为负号,不妨设这些元素的和为
n
e
g
neg
neg,那么加上负号后其和就为
−
n
e
g
-neg
−neg,显然其余元素的和为
s
u
m
−
n
e
g
sum-neg
sum−neg,由
(
s
u
m
−
n
e
g
)
+
(
−
n
e
g
)
=
t
a
r
g
e
t
(sum-neg)+(-neg)=target
(sum−neg)+(−neg)=target可得,
n
e
g
=
(
s
u
m
−
t
a
r
g
e
t
)
/
2
neg=(sum-target)/2
neg=(sum−target)/2。那么问题就可以转换为从数组中选任意个元素,使得它们的和为
n
e
g
neg
neg的方法数。是不是有点01背包的味道了?考虑
d
p
i
dp_i
dpi表示和为
i
i
i的方法数,显然对于第
j
j
j个数字,我们有:
d
p
k
=
d
p
k
+
d
p
k
−
n
u
m
s
j
dp_k=dp_k+dp_{k-nums_j}
dpk=dpk+dpk−numsj
由于每个数字只能选一次(01背包),因此
k
k
k需要逆着推。
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum=0,n=nums.size();
for(int i=0;i<n;i++)
sum+=nums[i];
sum-=target;
if(sum<0||sum&1)
return 0;
target=sum>>1;
vector<int> dp(target+1);
dp[0]=1;
for(int i=0;i<n;i++)
for(int j=target;j>=nums[i];j--)
dp[j]+=dp[j-nums[i]];
return dp[target];
}
};