• 力扣算法:目标和


    原题

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/target-sum

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

    思路:

      因为 +  -  是要出现在每个数字前边 , 所以我认为这是一颗慢二叉树。二叉树的深度与数组长度相同。所以我设定了一个栈,开始入栈五个 ‘+’  号。然后求和并判断。

    然后进入循环。如果栈顶元素是‘ - ’ 号,则出栈。之后的话如果栈不空。说明栈顶是 +  号,然后就将这个 + 号出栈,减号入栈。然后剩下的 用 + 号占位,使栈的长度与数组长度想同,计算结果,并比较。这样所有的情况就可以遍历完成。

    但是这种方法的复杂度是 2^N  ,当数组长度过高时,会产生超时。

    源代码如下:

    public int findTargetSumWays(int[] nums, int S) {
    
            Stack<Character> stack=new Stack<>();
            int result=0;     //结果数
            int sum=0;        //数组内求和   或者说是计算结果
            for(int i=0;i<nums.length;i++) {
                stack.push('+');   //入栈
                sum=sum+=nums[stack.size()-1];  //计算结果
            }
            if(sum==S) {  //判断
                result++;
            }
    
            while (true){
                while (!stack.isEmpty()&&stack.peek()=='-') {  // -号出栈
                    sum=sum+nums[stack.size()-1];     //因为是减号出栈,所以需要在原来的结果上加上这个数才是前几个数的计算结果。
                    stack.pop();
                }
    
                if (stack.size()>0){
                    sum=sum-nums[stack.size()-1];     //栈顶是 + 号,出栈的话需要减去这个数字。
                    stack.pop();
                    stack.push('-');                 // + 号换减号
                    sum=sum-nums[stack.size()-1];   //计算
                    while (stack.size()<nums.length) {   //长度不够用 + 号补齐
                        stack.push('+');
                        sum=sum+nums[stack.size()-1];    //计算
                    }
                }else{
                    break;
                }
                if(sum==S)   //筛选
                    result++;
            }
    
            return result;
        }

    由于上边的方法超时了,所以我参考了一下答案。用的是动态规划。具体如下

    设定数组 dp[i][j]  表示前 i 个数字,计算结果为 j 时的方案数。所以

    dp[i][j]=dp[i-1][j+nums[i]]+dp[i-1][j-nums[i]]

    可以写成递推式:dp[i][j+nums[i]]+=dp[i-1][j]

            dp[i][j-nums[i]]+=dp[i-1][j]

    由于数组的下标>=0,并且所有数字之和不大于1000 。 所以在列中每个要+1000.使其不要越界。

    源码如下:

    public int findTargetSumWays(int[] nums, int S) {
            int[][] dp=new int[nums.length][2001];
            dp[0][nums[0]+1000]=1;
            dp[0][-nums[0]+1000] +=1;
            for(int i=1;i<nums.length;i++){
                for(int sum= -1000;sum<=1000;sum++){
                    if(dp[i-1][sum+1000]>0){
                        dp[i][sum+nums[i]+1000] += dp[i-1][sum+1000];
    
                        dp[i][sum-nums[i]+1000] +=dp[i-1][sum+1000];
    
                    }
                }
    
            }
            if(S>1000)
                return 0;
            else
                return dp[nums.length-1][S+1000];
    
            
        }
  • 相关阅读:
    [JSOI2018]潜入行动
    [ZJOI2013]丽洁体
    [CTSC2017]吉夫特
    [SDOI2016]储能表
    POJ-3616 Milking Time
    POJ-2385 Apple Catching---DP
    POJ-2229 Sumsets---完全背包变形
    hdu-1114 Piggy-Bank---完全背包
    POJ-3050 Hopscotch---DFS
    POJ-3187 Backward Digit Sums---枚举全排列
  • 原文地址:https://www.cnblogs.com/wys-373/p/13269119.html
Copyright © 2020-2023  润新知