zoukankan      html  css  js  c++  java
  • 【LeetCode-背包】目标和

    题目描述

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

    题目链接: https://leetcode-cn.com/problems/target-sum/

    思路

    假设我们将数组分为两部分,两部分的和分别为 x,y,数组中所有元素的和为 sum,则有 x + y = sum, x - y = S,可得 x = (sum + S) / 2. 所以,问题就变为了数组能否分为两部分,其中一部分的和为 (sum + S) / 2。可以使用和等和子集一样的方法来做。

    • 状态定义:dp[i][j] 表示下标范围为 [0, i] 内数组元素中和为 j 的个数;
    • 状态转移:对于第 i 个数,可以选也可以不选,所以 dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]];
    • 边界条件:dp[i][0] = 1,含义是范围 [0, i] 内元素和为 0 的个数为 1,也就是所有的数都不选;dp[0][nums[0]] += 1 if nums[0]<=x,本来 dp[0][0] = 1,如果 nums[0] = 0,此时 dp[0][0] 就等于 2 了,也就可以选择 nums[0],也可以不选择 nums[0],因为 nums[0] = 0,所以这两种方法是一样的。

    代码如下:

    class Solution {
    public:
        int findTargetSumWays(vector<int>& nums, int S) {
            if(nums.empty() && S>0) return 0;
    
            int s = 0;
            for(int i=0; i<nums.size(); i++){
                s += nums[i];
            }
            if(s<S) return 0;
            s += S;
    
            if(s%2!=0) return 0;
            int target = s / 2;
    
            vector<vector<int>> dp(nums.size(), vector<int>(target+1, 0));
            for(int i=0; i<nums.size(); i++) dp[i][0] = 1; // 边界条件
            /*if(nums[0]<=target) dp[0][nums[0]] = 1;
            if(nums[0]==0) dp[0][nums[0]] = 2;*/
            if(nums[0]<=target) dp[0][nums[0]] += 1; // 这种写法和上面注释的写法是一样的
    
            for(int i=1; i<nums.size(); i++){
                for(int j=0; j<=target; j++){
                    dp[i][j] = dp[i-1][j];
                    if(j>=nums[i]){
                        dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]];
                    }
                }
            }
            return dp[nums.size()-1][target];
        }
    };
    

    空间复杂度优化:
    使用和等和子集一样的方法对空间复杂度进行优化:

    class Solution {
    public:
        int findTargetSumWays(vector<int>& nums, int S) {
            if(nums.empty() && S>0) return 0;
    
            int s = 0;
            for(int i=0; i<nums.size(); i++){
                s += nums[i];
            }
            if(s<S) return 0;
            s += S;
    
            if(s%2!=0) return 0;
            int target = s / 2;
    
            vector<int> dp(target+1, 0);
            dp[0] = 1;
            if(nums[0]<=target) dp[nums[0]] += 1;
            for(int i=1; i<nums.size(); i++){
                for(int j=target; j>=nums[i]; j--){
                    dp[j] += dp[j-nums[i]];
                }
            }
            return dp[target];
        }
    };
    
  • 相关阅读:
    02-0. 整数四则运算(10)
    中国大学MOOC-翁恺-C语言程序设计习题集
    树链剖分
    最小生成树---Prim
    最短路-spfa
    并查集
    Latex学习笔记 第一章
    Javaweb常用解决问题连接
    毕业论文如何排版
    毕业论文指之 “国内外研究现状”的撰写
  • 原文地址:https://www.cnblogs.com/flix/p/13574382.html
Copyright © 2011-2022 走看看