LeetCode 494. Target Sum
You are given an integer array nums
and an integer target
.
You want to build an expression out of nums by adding one of the symbols '+'
and '-'
before each integer in nums and then concatenate all the integers.
For example, if nums = [2, 1]
, you can add a '+'
before 2 and a '-'
before 1 and concatenate them to build the expression "+2-1"
.
Return the number of different expressions that you can build, which evaluates to target.
Example 1:
Input: nums = [1,1,1,1,1], target = 3
Output: 5
Explanation: There are 5 ways to assign symbols to make the sum of nums be target 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
+1 + 1 + 1 + 1 - 1 = 3
method 1: 动态规划
把nums
分成两堆,一堆left
,一堆right
,left
都给正号,right
都给负号,所以left-right=target
,又因为left+right=sum
是固定的,所以有left-(sum-left)=target
,即left=(target+sum)/2
dp[j]
:填满容量为j
的背包有dp[j]
种填法
转化为容量为(target+sum)/2
的背包有多少种装法,例如容量为3的背包有dp[3]
种装法,又来了一个weight[i]=2
,则容量为5的背包的装法应该加上dp[5-weight[i]]=dp[3]
,多一个物品nums[i]
,就会多dp[j-nums[i]]
种装法
求装满背包有几种方法,一般公式都是:
dp[j] += dp[j - nums[i]]
使用滚动数组,外层遍历物品nums[i]
,内层逆序遍历背包
初始化:dp[0]=1
,装满容量为0的背包,有1种装法,就是放0个物品
int findTargetSumWays(vector<int>& nums, int target) {
int sum = 0;
for (auto n : nums) sum += n;
if (abs(target) > sum) return 0; // 全都给正号或负号也不行
if ((target + sum) % 2) return 0; // 一奇一偶不行
int bagSize = (target + sum) / 2;
vector<int> dp(bagSize + 1);
dp[0] = 1;
for (int i = 0; i < nums.size(); i++) { // 遍历物品
for (int j = bagSize; j >= nums[i]; j--) { // 逆序遍历背包
dp[j] += dp[j - nums[i]];
}
}
return dp[bagSize];
}
如果sum
是奇数,不管怎么组合都组合不出偶数,sum
是奇数也组合不出偶数
method 2: 回溯
每个数有2种选择,要么加,要么减
int res;
void dfs(vector<int>& nums, int index, int target) {
if (index == nums.size()) {
if (target == 0) res++;
return;
}
dfs(nums, index + 1, target - nums[index]);
dfs(nums, index + 1, target + nums[index]);
}
int findTargetSumWays(vector<int>& nums, int target) {
dfs(nums, 0, target);
return res;
}