494. 目标和
给定一个非负整数数组,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。
运行结果
解题思路
利用子集划分思想
只有+和-。我们将数组分为A(+)和B(-)两个子集
那么sum(A)-sum(B)=S----->sum(A)=S+sum(B)=2S+sum(B)+sum(A)=2S+sum(nums)---已知数
这也就转换成了给定一个数组,求子集和为target有几种子集划分方式===典型的0-1背包问题
然后利用动态规划求解背包问题
dp[i][j]:在nums的前i个元素中子集和为j的结果数
具体见注释
可选优化---将二维数组降维到一维(有点难,有思路即可)
java代码
class Solution {
public int findTargetSumWays(int[] nums, int S) {
int sum=0;
for(int n:nums){
sum += n;
}
//无法子集划分
if(sum < S || (sum + S) % 2 == 1){
return 0;
}
return subsets(nums,(sum+S)/2);
}
//求数组中有多少子集和为target
public int subsets(int[] nums , int target){
int n=nums.length;
//dp[i][j]:在nums的前i个元素中子集和为j的结果数
int[][] dp=new int[n+1][target+1];
//dp[0][..]=0-----无元素无法完成,初值即为0
//base case:和为0只有空集
for(int i=0;i<=n;i++){
dp[i][0]=1;
}
for(int i=1;i<=n;i++){
for(int j=0;j<=target;j++){
if(j-nums[i-1] >= 0){
//对于装和不装该物品
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][target];
}
}
京公网安备 11010502036488号