494. 目标和
————————————————————————————————————————————
给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
示例 1:
输入: nums: [1, 1, 1, 1, 1], S: 3
输出: 5
解释:
-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
一共有5种方法让最终目标和为3。
注意:
数组的长度不会超过20,并且数组中的值全为正数。
初始的数组的和不会超过1000。
保证返回的最终结果为32位整数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/target-sum
————————————————————————————————————————————
这道题最直接的方法就是DFS深度优先遍历,不过可能超时,但是还有一个很棒的DP做法,我是参考网上的分析,主要分析如下:
1、该问题求解数组中数字只和等于目标值的方案个数,每个数字的符号可以为正或负(减整数等于加负数)。
2、该问题和矩阵链乘很相似,是典型的动态规划问题
3、举例说明: nums = {1,2,3,4,5}, target=3, 一种可行的方案是+1-2+3-4+5 =3
该方案中数组元素可以分为两组,一组是数字符号为正(P={1,3,5}),另一组数字符号为负(N={2,4})
因此: sum(1,3,5) - sum(2,4) = target
sum(1,3,5) - sum(2,4) + sum(1,3,5) + sum(2,4) = target + sum(1,3,5) + sum(2,4)
2sum(1,3,5) = target + sum(1,3,5) + sum(2,4)
2sum§ = target + sum(nums)
sum§ = (target + sum(nums)) / 2
由于target和sum(nums)是固定值,因此原始问题转化为求解nums中子集的和等于sum§的方案个数问题
4、求解nums中子集合只和为sum§的方案个数(nums中所有元素都是非负)
该问题可以通过动态规划算法求解
举例说明:给定集合nums={1,2,3,4,5}, 求解子集,使子集中元素之和等于9 = new_target = sum§ = (target+sum(nums))/2
定义dp[10]数组, dp[10] = {1,0,0,0,0,0,0,0,0,0}
dp[i]表示子集合元素之和等于当前目标值的方案个数, 当前目标值等于9减去当前元素值
当前元素等于1时,dp[9] = dp[9] + dp[9-1]
dp[8] = dp[8] + dp[8-1]
…
dp[1] = dp[1] + dp[1-1]
当前元素等于2时,dp[9] = dp[9] + dp[9-2]
dp[8] = dp[8] + dp[8-2]
…
dp[2] = dp[2] + dp[2-2]
当前元素等于3时,dp[9] = dp[9] + dp[9-3]
dp[8] = dp[8] + dp[8-3]
…
dp[3] = dp[3] + dp[3-3]
当前元素等于4时,
…
当前元素等于5时,
…
dp[5] = dp[5] + dp[5-5]
最后返回dp[9]即是所求的解
————————————————
版权声明:本文为优快云博主「JackZhangNJU」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/JackZhang_123/article/details/78793365
————————————————————————————————————————————
这道题就是转化一下,转化为求数组子集等于某个值的方法数。dp[i]表示数组子集之和为i的方法数的数量。dp[i]初始化为0(dp[0]初始化为1,意思是数组之和为0的方法数是1个)。然后对于数组中的每个数,计算dp[i] = dp[i] + dp[i-nums[j]],比如dp[9]的意思是数组子集之和为9的方法数,这时候对于数组nums中的某一个数2,则有dp[9] = dp[9] + dp[7],意思是当子集中有一个数是2时,其余数据相加之和为7的方法数,进行求和统计。其C++代码如下:
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int s) {
int all = accumulate(nums.begin(),nums.end(),0); #计算nums之和
if(all<s||(all+s)%2!=0)
return 0;
int length = nums.size();
int p = (all + s) / 2;
vector<int> dp(p+1,0);
dp[0] = 1;
for(int i=0;i<length;i++)
{
for(int j=p;j>=nums[i];j--)
dp[j] = dp[j] + dp[j-nums[i]];
}
return dp[p];
}
};