zoukankan      html  css  js  c++  java
  • LeetCode——目标和

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

    注意:
    数组非空,且长度不会超过20。
    初始的数组的和不会超过1000。
    保证返回的最终结果能被32位整数存下。

    A:
    1.递归
    这个题虽然是典型的0-1背包,但由于有负数加入,实际上用递归比较好。

        private int count;
    
        public int findTargetSumWays(int[] nums, int S) {
            count = 0;
            calculate(nums, S, 0);
            return count;
        }
    
        private void calculate(int[] nums, int S, int i) {
            if (i == nums.length) {
                if (S == 0) {
                    count++;
                }
                return;
            }
            calculate(nums, S - nums[i], i + 1);
            calculate(nums, S + nums[i], i + 1);
        }
    

    2.动态规划
    状态转移方程:

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

    解释:j+nums[i]和j-nums[i]表示对nums[i]可以执行加,还可以执行减,那么dp[i][j]的结果值就是加/减之后对应位置的和。

    明确base case:(关键
    例如,在示例nums = [1, 1, 1, 1, 1]中在非负整数数组的前提下,由于为是要选择+或者-,那么:

    • 组成的最大和就是对所有元素全选+。sum = 5
    • 组成的最小和的是对所有元素全选-。sum = -5

    说明我们的目标和S一定是 -5<= S <=5的,其范围长度length = sum*2 + 1。这样就确定下状态了。那我们的dp表就是:

    因为整个状态的范围长度length = sum*2 + 1,而j是从0开始的,故其初始化的位置应为sum。由于状态转移方程为dp[i][j] = dp[i-1][j+nums[i]] + dp[i-1][j-nums[i]].故dp表初始化应为:

    //如果nums[0]==0的话,那么+0和-0都应该算作其操作,故if(nums[0] == 0) dp[0][sum] = 2;
    if(nums[0] == 0) dp[0][sum] = 2;
    else{
        dp[0][sum+nums[0]] = 1;
        dp[0][sum-nums[0]] = 1;
    }
    

    最后结果:

        public int findTargetSumWays(int[] nums, int S) {
            int sum = 0;
            for (int num : nums)
                sum += num;
    
            if (Math.abs(S) > Math.abs(sum))
                return 0;
    
            int len = nums.length;
            // - 0 +
            int t = sum * 2 + 1;
            int[][] dp = new int[len][t];
            //初始化
            if (nums[0] == 0) {
                dp[0][sum] = 2;//0为正负
            } else {
                dp[0][sum + nums[0]] = 1;
                dp[0][sum - nums[0]] = 1;
            }
    
            for (int i = 1; i < len; i++) {
                for (int j = 0; j < t; j++) {
                    //注意边界
                    int l = (j - nums[i]) >= 0 ? j - nums[i] : 0;
                    int r = (j + nums[i]) < t ? j + nums[i] : 0;
                    dp[i][j] = dp[i - 1][l] + dp[i - 1][r];
                }
            }
            return dp[len - 1][sum + S];
        }
    
  • 相关阅读:
    论语学习系列(一)
    如何编译生成 dll
    Lua学习系列(二)
    Lua学习系列(一)
    Promise对象
    ... 扩展运算符
    class类
    proxy [ˈprɒksi] 代理
    HBuilder 打包流程
    Generator [ˈdʒenəreɪtə(r)] 函数结构
  • 原文地址:https://www.cnblogs.com/xym4869/p/13030487.html
Copyright © 2011-2022 走看看