494. 目标和(动态规划,01背包)
给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
示例:
输入: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 位整数存下。
通过次数55,907提交次数125,631
回溯超时:
class Solution { public: void backtrace(int sum,int S,vector<int>& nums,int pos) { //符合条件直接添加返回 if(sum == S && pos == nums.size()){ result++; return; } if(pos >= nums.size()) return; //第i个数取+ sum += nums[pos]; backtrace(sum,S,nums,pos+1); sum -= nums[pos]; //第i个数取- sum -= nums[pos]; backtrace(sum,S,nums,pos+1); sum += nums[pos]; } int findTargetSumWays(vector<int>& nums, int S) { backtrace(0,S,nums,0); return result; } public: int result = 0; };
dp:要转化成dp
class Solution { public: //dp系列:01背包 //dp[i][j] = dp[i-1][j-nums[i]] + dp[i-1][j+nums[i]] 这种dp转移相对麻烦一些 //另一种想法:数组分为正负两部分. 例如1 3 5 为加 sum(P) 2 4为- sum(N) //sum(P) + sum(N) = S //sum(P) - sum(N) = sum(nums) //sum(P) - sum(N) + sum(P) + sum(N) = S + sum(nums) ==> 2*sum(P) = s+sum(nums) ==> sum(P) = (S+sum(nums))/2 //典型的01背包,从nums中取 k个数使得和P 为(S+sum(nums))/2 int findTargetSumWays(vector<int>& nums, int S) { int n = nums.size(); int sum = accumulate(nums.begin(),nums.end(),0); long s = long(sum) + long(S); if(s % 2 || sum < S) return 0; vector<vector<int>> dp(n+1,vector<int>(s/2+1,0)); for(int i=1;i<=n;i++){ dp[i][0] = 1; } for(int j=1;j<=s/2;j++){ dp[0][j] = 0; } dp[0][0] = 1; for(int i=1;i<=n;i++){
//j从0开始,否则 dp[1][0]对于某些情况失败,[0 0 1] 1中dp[1][0] = 2 for(int j=0;j<=s/2;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][s/2]; } };