【leetcode494】目标和 Java题解

leetcode分类下所有的题解均为作者本人经过权衡后挑选出的题解,在易读和可维护性上有优势

每题只有一个答案,避免掉了太繁琐的以及不实用的方案,所以不一定是最优解

 

给定一个非负整数数组,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。

注意:

  1. 数组的长度不会超过20,并且数组中的值全为正数。
  2. 初始的数组的和不会超过1000。
  3. 保证返回的最终结果为32位整数。

递归解法

class Solution {
    int res = 0;
    public int findTargetSumWays(int[] nums, int S) {
        helper(nums, S, 0);  //递归
        return res;
    }
    public void helper(int[] nums, int S, int p){
        if(p >= nums.length)
        {
            if(S == 0) res++;
            return ;
        }
        helper(nums, S - nums[p], p + 1);
        helper(nums, S + nums[p], p + 1);
    }
}

dp解法

class Solution {
    public int findTargetSumWays(int[] nums, int S) {
        int sum = 0;
        for(int num : nums)
            sum += num;
        if(sum < S || (S + sum) % 2 != 0)
            return 0;
        return subsetSum(nums, (S + sum) / 2);
    }
    public int subsetSum(int[] nums, int s){  //以s这个数为和的子集有多少个
        int[] dp= new int[s + 1];
        dp[0] = 1;  //以0为和的子集有一个,是空集
        for(int num : nums)
            for(int i = s; i >= num; i--)
                dp[i] += dp[i - num];
        return dp[s];
    }
}

思路:

  • 递归思路比较简单,将每种可能性都走了一遍,由主函数返回那个正确的
  • dp思路需要先推导一个公式:
  •  sum(P) - sum(N) = target
     sum(P) + sum(N) + sum(P) - sum(N) = target + sum(P) + sum(N)
                            2 * sum(P) = target + sum(nums)
  • 原问题等同于:找到nums一个正子集和一个负子集,使得总和等于target。
    找到nums的一个子集P,使得sum(P) = (target + sum(nums))/2
  • 得到sum后,先判断一些基础条件是否成立,比如sum不能
  • 用return跳到另一个函数subsetSum中解决,记得把(S + sum) / 2的值带过去
  • 而subsetSum的处理结果就是,每个位置都放着对应索引值为和的所有组合,比如以0为和的子集有一个,是空集,所以dp[0]位置放了1

 

 

你可能感兴趣的:(Leetcode)