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];
    }
}