首页 > 其他分享 >力扣-494. 目标和

力扣-494. 目标和

时间:2024-06-04 10:36:46浏览次数:10  
标签:target nums int neg sum 目标 力扣 494 dp

1.题目

题目地址(494. 目标和 - 力扣(LeetCode))

https://leetcode.cn/problems/target-sum/

题目描述

给你一个非负整数数组 nums 和一个整数 target

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

  • 例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1"

返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

 

示例 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

2.题解

2.1 数学+动态规划(一维dp数组)

思路

首先我们如果思考想将这一题转换为一个动态规划问题,或者说一个0-1背包问题
我们必须明确选/不选什么类型的元素,这里有要加的元素/要减的元素两种,那么我们有必要设计一个二维dp数组,分别来记录这两种元素吗?
答案是没有必要的,由于+元素/-元素和target之间必然存在某种数学关系,我们可以通过这种数学关系来简化计算

首先我们假设sum = 所有元素的和(当作unsigned,无论符号),那么我们假设-元素和(这里的和为单纯的元素和)为neg, +元素便为sum - neg
target = +元素 - (-元素)= sum - neg - neg = sum - 2*neg
neg = (sum - target) / 2
其中sum很好求,我们简化使用accumulate函数,注意一共三个参数:数组的首位迭代器和初始值

接下来我们便可以利用动态规划来求解能组成元素和为neg的组合了
dp[i] = (在[0,i]个元素中任意选择元素,和为...), 如果我们使用这种传统思维,便会发现其中的局限性,如何在和为茫茫dp数组中寻找和为neg的那个组合?
我们逆转思路,dp[i]=(表示和为i的组合个数),且每一次新的组合都可以是基于前面已经列出组合 + 一个新元素 = 组成的新组合, 也就是有了递推关系
且最后我们寻找和为neg的组合个数时,直接求dp[neg]即可

之前我们的 查找条件(在索引[0,i]之间寻找,), 查找结果(根据题目而定)
这里还是遵循 查找条件(和为neg), 查找结果(组合个数)

代码1(使用二维dp)

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for (int& num : nums) {
            sum += num;
        }
        int diff = sum - target;
        if (diff < 0 || diff % 2 != 0) {
            return 0;
        }
        int n = nums.size(), neg = diff / 2;
        vector<vector<int>> dp(n + 1, vector<int>(neg + 1));
        dp[0][0] = 1;
        for (int i = 1; i <= n; i++) {
            int num = nums[i - 1];
            for (int j = 0; j <= neg; j++) {
                dp[i][j] = dp[i - 1][j];
                if (j >= num) {
                    dp[i][j] += dp[i - 1][j - num];
                }
            }
        }
        return dp[n][neg];
    }
};

代码2(简化,使用一维dp)

  • 语言支持:C++
    C++ Code:
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int n = nums.size();
        int sum = accumulate(nums.begin(), nums.end(), 0);
        if(sum - target < 0 || (sum - target) & 1) return 0;
        int neg = (sum - target) / 2;
        vector<int> dp(neg + 1);
        dp[0] = 1; // 什么都不选是一种选择方式
        for(int i = 0; i < n; i++){
            int num = nums[i];
            // 为了避免覆盖,使用倒序更新
            for(int j = neg; j >= num; j--){
                dp[j] += dp[j - num];
            }
        }
        return dp[neg];
    }
};

复杂度分析

  • 时间复杂度:\(O(n\times(sum-target))\),其中\(n\)是数组nums的长度,sum是数组nums的元素和,target是目标数。动态规划有\((n+1)\times(\frac{sum-target}2+1)\)个状态,需要计算每个状态的值。

  • 空间复杂度:\(O(sum-target)\),其中sum是数组nums的元素和,target是目标数。使用空间优化的实现,需要创建长度为\(\frac{sum-target}2+1\)的数组\(dp\) 。

2.2 回溯

思路

要么加,要么减,我们可以考虑利用回溯遍历所有可能性,在一种可能性结束后,进行回溯,执行另一种可能性。

代码

class Solution {
public:
    int count = 0;
    int findTargetSumWays(vector<int>& nums, int target) {
        backtrack(nums, target, 0, 0);
        return count;
    }

    void backtrack(vector<int> &nums, int target, int index, int sum){
        if(index == nums.size()){
            if(sum == target){
                count++;
            }
        }else{
            backtrack(nums, target, index + 1, sum + nums[index]);
            backtrack(nums, target, index + 1, sum - nums[index]);
        }
    }
};

复杂度分析

  • 时间复杂度:\(O(2^n)\),其中\(n\)是数组nums的长度。回溯需要遍历所有不同的表达式,共有\(2^n\)种不同的表达式,每种表达式计算结果需要\(O(1)\)的时间,因此总时间复杂度是\(O(2^n)\)。

  • 空间复杂度:\(O(n)\),其中\(n\)是数组nums的长度。空间复杂度主要取决于递归调用的栈空间,栈的深度不超过\(n\)。

标签:target,nums,int,neg,sum,目标,力扣,494,dp
From: https://www.cnblogs.com/trmbh12/p/18230242

相关文章

  • 力扣每日一题 6/3
    1103.分糖果II[简单]题目:排排坐,分糖果。我们买了一些糖果 candies,打算把它们分给排好队的 n=num_people 个小朋友。给第一个小朋友1颗糖果,第二个小朋友2颗,依此类推,直到给最后一个小朋友 n 颗糖果。然后,我们再回到队伍的起点,给第一个小朋友 n +1 颗糖果,第二......
  • 算法第四天力扣第704题:二分查找
    704.二分查找的题目链接如下:https://leetcode.cn/problems/binary-search/https://leetcode.cn/problems/binary-search/  给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 ......
  • 在MySQL中,你可以使用动态SQL和存储过程来根据元数据表查询多个表,并将结果集合并。以下
    DELIMITER$$CREATEPROCEDUREMergeDataFromTables()BEGIN--游标声明DECLAREdoneINTDEFAULTFALSE;DECLAREtbl_nameVARCHAR(255);DECLAREcurCURSORFORSELECT表明FROMtable_col;DECLARECONTINUEHANDLERFORNOTFOUNDSETdone=TRU......
  • 力扣第五题 5.最长回文子串
    目录问题解题思路动态规划中心扩展官方解法1.动态规划2.中心扩展算法3.Manacher 算法问题解题思路我们的回文子串有两种情况,一种是左与右相同,一种是左与右+1的位置所以我们就可以根据这个条件判断是否为子串,然后再扩大判断。还可以使用中心扩展的方式,就判断左......
  • 力扣1168水资源优化
    题目这个题目首先有节点,有双向边,而且要求最少总成本,那么我们最先想到的应该是最小生成树。算法逻辑 在最小生成树中有一个prim算法,个人觉得是和dijkstra非常相似甚至一模一样的,基于贪心思想的一种算法。prim的算法过程:首先找到一个一定存在的节点,然后从这个结点开始......
  • 力扣2891每日一题题解
    题目:给你一个仅由小写英文字母组成的字符串 s 。如果一个字符串仅由单一字符组成,那么它被称为 特殊 字符串。例如,字符串 "abc" 不是特殊字符串,而字符串 "ddd"、"zz" 和 "f" 是特殊字符串。返回在 s 中出现 至少三次 的 最长特殊子字符串 的长度,如果不存在出......
  • (自用)目标检测的一些方法
    一、Numpy模块importnumpyasnpnp.arange()生成一维数据numpy.arange(start,stop,step,dtype=None)     start:开始数字,默认为0     stop:停止数字(不输出该值),必填     step:步长,默认为1np.reshape()改变数组或矩阵的形状numpy.resha......
  • 3D目标检测入门:探索OpenPCDet框架
    前言在自动驾驶和机器人视觉这两个飞速发展的领域中,3D目标检测技术扮演着核心角色。随着深度学习技术的突破性进展,3D目标检测算法的研究和应用正日益深入。OpenPCDet,这个由香港中文大学OpenMMLab实验室精心打造的开源工具箱,为3D目标检测领域提供了一个功能强大且易于使用的平......
  • [目标检测数据集]变电站缺陷检测数据集8307张17类别VOC和YOLO格式
    数据集格式:PascalVOC格式+YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件)图片数量(jpg文件个数):8307标注数量(xml文件个数):8307标注数量(txt文件个数):8307标注类别数:17标注类别名称:[“bj_bpmh”,“bj_bpps”,“bj_wkps”,......
  • 开源代码分享(32)-基于改进多目标灰狼算法的冷热电联供型微电网运行优化
    参考文献:[1]戚艳,尚学军,聂靖宇,等.基于改进多目标灰狼算法的冷热电联供型微电网运行优化[J].电测与仪表,2022,59(06):12-19+52.DOI:10.19753/j.issn1001-1390.2022.06.002.1.问题背景        针对冷热电联供型微电网运行调度的优化问题,为实现节能减排的目标,以微电......