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.
题意分析
给定一个数组,给每个元素加上符号+或-,使得所有元素的和为S,输出满足条件的符号分配方案总数。
解法分析
这道题是0,1背包问题的扩展问题,0,1背包原问题如下,给定一组物品的重量,以及背包容量,求问最大可容纳的物品重量,该问题用动态规划的方法解决,令dp[i][j]表示下标i(包含i)物品之前的所有物品来放入背包,j表示现有背包容量,该问题具有最优子结构,dp[i][j]=max(dp[i-1][j],dp[i-1][j-nums[i]),表示放入nums[i]和不放入nums[i]的情况,注意如果j<nums[i],只能取dp[i-1][j],注意初始条件的设置,用自底向上的动态规划完成。如Leetcode 416 Partition Equal Subset Sum,就可以看做找一个子集,使得他们的和为sum的一半,dp[i][j]是一个bool值,dp[i][j]=dp[i-1][j]|dp[i-1][j-num[i]),虽不是最优化问题,但仍然可以利用子结构。C++代码如下:
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum=0;
for(auto s:nums)
sum+=s;
if(sum%2!=0)
return false;
int half=sum/2;
int n=nums.size();
bool dp[n+1][half+1];
int i,j;
for(j=1;j<=half;j++)
dp[0][j]=false;
for(i=0;i<=n;i++)
dp[i][0]=true;
for(i=1;i<=n;i++){
for(j=0;j<=half;j++){
if(j>=nums[i-1])
dp[i][j]=dp[i-1][j-nums[i-1]]||dp[i-1][j];
else
dp[i][j]=dp[i-1][j];
}
}
return dp[n][half];
}
};
本题其实也是找一个子集,使得子集的和满足一个target,令符号为正的元素和为P,负的和为N,则P-N=S,P=N+S,而P+N=Sum,所以P=(S+Sum)/2,这里可以将非偶的情况直接输出0.dp[i][j]表示target为j,下标为i之前元素子集为j的组合个数,C++代码如下:
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int S) {
int n=nums.size();
int sum=0;
for(auto s:nums)
sum+=s;
if(S>sum)
return 0;
int positive=sum+S;
if(positive%2!=0)
return 0;
positive=positive/2;
vector<vector<int>> dp(n+1,vector<int>(positive+1,0));
//int dp[n+1][positive+1];
int i,j;
for(i=0;i<=n;i++)
dp[i][0]=1;
for(i=1;i<=n;i++){
for(j=0;j<=positive;j++){
if(j>=nums[i-1])
dp[i][j]=dp[i-1][j]+dp[i-1][j-nums[i-1]];
else
dp[i][j]=dp[i-1][j];
}
}
return dp[n][positive];
}
};