力扣-动态规划-494. 目标和

27 篇文章 0 订阅
26 篇文章 0 订阅

力扣-动态规划-494. 目标和

494. 目标和

题目描述

给你一个整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :

例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/target-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

示例 1:
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 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
+1 + 1 + 1 + 1 - 1 = 3

示例 2:
输入:nums = [1], target = 1
输出:1

提示:
1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 1000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/target-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:动态规划 01背包

 昨天写博客总感觉缺少了点什么,今天必须把博客写上。
 今天接着刷01背包的题目,讲真,这道题卡了我很久,最后花了一个下午的时间总算弄出来了。然后对比答案,发现答案的思路更妙。我自己的思路我觉得更容易想到,但存在初始化比较复杂的问题,在初始化的时候得考虑周全才能通过所有的测试用例。
 根据问题很容易想到每个元素有两种状态,加上绝对值或者减去绝对值。首先遍历每个元素求得绝对值总和为sum,因此dp(i,j)表示从下标为0到i的元素中组成的不同表达式的种类的个数,其中j在逻辑上表示绝对值总和,在实际代码中会转化为列的下标。
 我自己写的代码如下,代码已经加了注释,各位小伙伴如果有什么问题可以在评论里提出来,欢迎大家交流。

//自己的思路  将问题转化为01背包的类似问题-构建一个二维矩阵
    //元素的值有两种选择 +元素值 -元素值,最后得到的总和target相当于背包容量
    //dp[i][j]相当于从0-i的元素下表中选择总和为j的表达式的数量
    //dp[i][j] = dp[i - 1][j - value[j]] + dp[i - 1][j + value[j]];
    //外层是元素,内层是元素总和 从上到下逐层遍历
    //举例推导 ok
    //初始化较为复杂 判断是否等于sum 判断是否是0
    public int findTargetSumWays(int[] nums, int target) {
        //首先统计所有元素的绝对值之和,得到元素和的最大和最小范围
        int sum = 0;
        int zeroNum = 0;
        for (int ele : nums) {
            sum += Math.abs(ele);        
        }
        if (target < -sum || target > sum) {//target不在元素能得到的和的范围之内
            return 0;
        }
        int length = sum + sum + 1;//列的数量
        int[][] dp = new int[nums.length][length];
        //初始化第一列
        int tempSum = 0;       
        int tempZero = 0;//统计前面0的数量
        for (int i = 0; i < nums.length; i++) {
            tempSum += Math.abs(nums[i]);  
            if (nums[i] == 0) {
                tempZero++;                   
            }         
            if (tempSum == sum || tempSum == -sum) {                
                dp[i][0] = (int)Math.pow(2, tempZero);          
            }
        }
        dp[0][length - 1] = dp[0][0];
        //初始化第一行
        if (nums[0] == 0) {
            dp[0][nums[0] + sum] = 2;
        } else {
            dp[0][nums[0] + sum] = 1;
            dp[0][sum - nums[0]] = 1;
        }
        for (int i = 1; i < nums.length; i++) {
            for (int j = 1 - sum; j <= sum; j++) {
                int a = j + sum + nums[i];
                int b = j + sum - nums[i];         
                int add = 0;//代表加的方式
                int sub = 0;//代表减的方式
                if (a < 0 || a >= length) { 
                    add = 0;                                            
                } else {
                    add = dp[i-1][j + sum + nums[i]];
                }         
                if (b < 0 || b >= length) {
                    sub = 0;
                } else {
                    sub = dp[i-1][j + sum - nums[i]]; 
                }
                dp[i][j + sum] = add + sub;              
            } 
        }
        return dp[nums.length - 1][target + sum];
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值