LeetCode494. 目标和

494. 目标和

文章目录

    • [494. 目标和](https://leetcode.cn/problems/target-sum/)
      • 一、题目
      • 二、题解
        • 方法一:目标和路径计数算法
        • 方法二:01背包
        • 方法三:01背包一维数组


一、题目

给你一个非负整数数组 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

二、题解

方法一:目标和路径计数算法

思考过程可以分为以下几步:

  1. 问题拆解: 首先,将问题拆解为更小的子问题。在这个问题中,我们可以将目标和问题拆解为在给定数组中选择一些数字,使得它们的和等于目标值。

  2. 状态定义: 确定动态规划的状态。在这个问题中,我们可以考虑使用二维数组 dp[i][j] 表示在前 i 个数字中,和为 j 的表达式的数量。

  3. 状态转移方程: 找到状态之间的转移关系。这是动态规划问题的核心。在这个问题中,对于每个数字 nums[i],我们有两种选择:添加正号或者添加负号。因此,状态转移方程可以表示为:dp[i][j] = dp[i-1][j-nums[i]] + dp[i-1][j+nums[i]],即前 i-1 个数字和为 j-nums[i] 的表达式数量加上前 i-1 个数字和为 j+nums[i] 的表达式数量,最后的数字是dp[i][j] = dp[i-1][abs(j-nums[i])] + dp[i-1][j+nums[i]],因为如果j < nums[i],我们是可以通过dp[i-1][nums[i]-j]得到dp[i][j]。举个例子,如图,为什么dp[2][0] = 2?图中已经说明白了。

在这里插入图片描述

  1. 边界条件: 根据问题的实际情况,确定边界条件。在这个问题中,我们可以从第一个数字开始考虑,初始化 dp[0][j],表示只有一个数字时,和为 j 的表达式数量。

  2. 问题求解: 根据状态转移方程和边界条件,从小问题逐步求解到原问题。填充二维数组 dp,最终 dp[nums.size()-1][abs(target)] 就是我们要的答案,表示在所有数字中,和为 target 的表达式数量。

class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {// 定义一个二维数组 dp,大小为 (nums.size(), 2001),其中 dp[i][j] 表示在前 i 个数字中,和为 j 的表达式数量vector<vector<int>> dp(nums.size(), vector<int>(2001, 0));// 初始化:处理只有一个数字的情况for(int i = 0; i <= 1000; i++){if(nums[0] == i){if(nums[0] == 0) dp[0][i] = 2;  // 对于数字 0,可以有正号或负号else dp[0][i] = 1;}}// 填写 dp 数组for(int i = 1; i < nums.size(); i++){for(int j = 0; j <= 1000; j++){dp[i][j] = dp[i-1][abs(j-nums[i])] + dp[i-1][j+nums[i]];  // 根据状态转移方程计算 dp[i][j]}}return dp[nums.size()-1][abs(target)];  // 返回在所有数字中,和为 target 的表达式数量}
};

方法二:01背包

01背包问题通常是这样的:给定一组物品,每个物品有重量和价值,我们要选择一些物品放入一个容量有限的背包中,使得背包中物品的总重量不超过背包的容量,同时最大化物品的总价值。

在这道题中,我们可以将正数部分和负数部分(前面是加号的和前面是减号的)的数字分别看作两组物品。正数部分的数字相当于具有正的价值,负数部分的数字相当于具有负的价值。

具体步骤如下:

  1. 将数组 nums 拆分成两个子数组:addminusadd 包含所有正数部分的数字,minus 包含所有负数部分的数字。

    我们可以得到add-minus = targetadd+minus = sum,从而根据这两个式子得出(sum+target)/2 = add

  2. 使用01背包的思想:这个问题就转化成了所有数字凑成add这个数的方法。即背包容量是add,物品是nums数组里的所有元素。

  3. 使用动态规划来解决问题:我们创建一个二维数组 dp,其中 dp[i][j] 表示在考虑前 i 个物品时,总和等于 j 的方法数。

    • 初始化 dp[0][0] 为 1,因为当没有物品可选时,总和为 0 是唯一的一种方式;但如果nums[0] = 0,还有一种方式就是只取nums[0],则有两种方式。
    • 对于其它物品 nums[i],我们根据以下规则更新 dp[i][j]
      • 如果 j >= nums[i],那么我们可以选择是否将 nums[i] 放入背包,dp[i][j]等于不放入背包和放入背包方式的总和,即 dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]]
      • 如果 j < nums[i],则nums[i]不可以放入背包dp[i][j] = dp[i-1][j]
  4. 最终的答案是 dp[nums.size()-1][add],表示在考虑所有物品后,总和等于 add 的方法数。

class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {int sum = 0;// 计算数组nums的总和for(int i = 0; i < nums.size(); i++){sum += nums[i];}// 如果总和与目标值之和为奇数,或者总和小于目标值的绝对值,则无解,返回0if((sum + target) % 2 == 1 || sum < abs(target)){return 0;}// 计算要构造的正数部分和,这是01背包问题中的背包容量int add = (sum + target) / 2;// 创建二维动态规划数组dpvector<vector<int>> dp(nums.size(),vector<int>(add+1, 0));// 初始化dp数组for(int i = 0; i <= add; i++){// 如果i等于第一个数字nums[0],表示可以用第一个数字构造和为i的方式有1种if(i == nums[0]){dp[0][i] = 1;}} // 特殊情况处理if(nums[0] == 0) dp[0][0] = 2;// 普通情况:当没有物品可选时,总和为 0 是唯一的一种方式else dp[0][0] = 1;// 填写dp数组for(int i = 1; i < nums.size(); i++){for(int j = 0; j <= add; j++){// 如果当前数字nums[i]大于目标和j,无法用当前数字构造if(nums[i] > j) dp[i][j] = dp[i-1][j];// 选择是否将 `nums[i]` 放入背包,`dp[i][j]`等于不放入背包和放入背包方式的总和else dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]];}}// 返回最终的结果,即在考虑所有数字后,构造和为add的方法数return dp[nums.size()-1][add];}
};

方法三:01背包一维数组

具体细节就是初始化这里,这个dp[0] = 1相当于是按照结论推初始化(因为按照二维的初始化方式是错误的),最好还是去理解一下二维的吧……

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/64726.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面试官:说一下 MyBatis 的一级缓存和二级缓存 ?

目录 1. MyBatis 的缓存机制 2. 为什么不默认开启 MyBatis 的二级缓存 3. MyBatis 如何开启二级缓存 4. MyBatis 有哪些缓存清除策略 1. MyBatis 的缓存机制 MyBayis 中包含两级缓存&#xff1a;一级缓存和二级缓存 1. 一级缓存是 SqlSession 级别的&#xff0c;是 MyBati…

jmeter 常数吞吐量定时器

模拟固定吞吐量的定时器。它可以控制测试计划中各个请求之间的时间间隔&#xff0c;以达到预期的吞吐量。 参数包括&#xff1a; Target Throughput&#xff1a;目标吞吐量&#xff08;每分钟请求数&#xff09;Calculate Throughput based on&#xff1a;吞吐量计算基准&…

stm32---用外部中断实现红外接收器

一、红外遥控的原理 红外遥控是一种无线、非接触控制技术&#xff0c;具有抗干扰能力强&#xff0c;信息传 输可靠&#xff0c;功耗低&#xff0c;成本低&#xff0c;易实现等显著优点&#xff0c;被诸多电子设备特别是 家用电器广泛采用&#xff0c;并越来越多的应用到计算机系…

2022年09月 C/C++(六级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C编程&#xff08;1~8级&#xff09;全部真题・点这里 第1题&#xff1a;stack or queue 栈和队列都是常用的线性结构&#xff0c;它们都提供两个操作&#xff1a; Push&#xff1a;加入一个元素。 Pop&#xff1a;弹出一个元素。 不同的是&#xff0c;栈是”先进后出”&…

leetcode236. 二叉树的最近公共祖先(java)

二叉树的最近公共祖先 题目描述递归法代码演示 上期经典 题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q …

Ae 效果:CC Light Rays

生成/CC Light Rays Generate/CC Light Rays CC Light Rays&#xff08;CC 光线&#xff09;可以创建从光源发出并能穿过图层内容的光线效果。常用于制作光线透过门窗或云层的场景&#xff0c;或者用于创建神奇或梦幻的氛围感。 本效果会被限制在源图层的大小范围之内。 ◆ ◆…

每日一题 98验证二叉搜索树(中序遍历)

题目 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例 1&#xff1a…

【负载均衡】常见的负载均衡策略有哪些?

文章目录 前言负载均衡分类常见负载均衡策略小结 前言 负载均衡策略是实现负载均衡器的关键&#xff0c;而负载均衡器又是分布式系统中不可或缺的重要组件。使用它有助于提高系统的整体性能、可用性、可靠性和安全性&#xff0c;同时支持系统的扩展和故障容忍性。对于处理大量…

基于 Docker 的 MySQL 主从复制搭建(Mac M1版本)

系统&#xff1a;Macbook M1 镜像版本&#xff1a;mysql:5.7 如果是要查 slave连接不上 master的问题&#xff0c;可以直接跳到文章末尾踩坑处 准备工作 拉取镜像 docker pull mysql:5.7本地数据卷挂载 因为mysql不挂载的话&#xff0c;重启丢失数据&#xff0c;所以在本地创…

微服务--Gatway:网关

routes: - id:order_route(路由唯一 标识&#xff0c;路由到order) uri&#xff1a;http://localhost:8020 #需要转发的地址 #断言规则&#xff08;用于路由规则的匹配&#xff09; predicates: -path/order-serv/** -pathlb://order-service # lb: 使用nacos中的本地…

分享码云上8个宝藏又有价值的开源图片编辑器

如果你需要高效地处理图片&#xff0c;那么这8款实用工具是可以尝试的&#xff01; 它们能够进行一键抠图、放大、拼接、转矢量图、图标自动生成以及等操作&#xff0c;让你的工作效率飞升&#xff01; 在Gitee这个最有价值的开源项目计划是Gitee综合评定出的优秀开源项目的展示…

springboot web开发springmvc自动配置原理

前言 我们也知道springboot启用springmvc基本不用做什么配置可以很方便就使用了但是不了解原理,开发过程中遇到点问题估计就比较头疼,不管了解的深不深入,先巴拉一番再说… 下面我们先看看官网…我的版本是2.3.2版本,发现官网改动也比较大…不同版本自己巴拉下吧,结构虽然变化…

C++文件操作

一、fstream简介 C 提供了一组用于文件操作的标准库fstream&#xff0c;可以进行文件的读取、写入和其他相关操作。常用的文件操作包括文件的打开、关闭、读取、写入和定位等。下面是一些常见的文件操作函数&#xff1a; 文件的打开和关闭&#xff1a; std::ofstream&#x…

leetcode 941. 有效的山脉数组

2023.9.2 可以用双指针法来做&#xff0c;left指向数组起点&#xff0c;right指向数组终点&#xff0c;left满足条件则左移&#xff0c;right满足条件则右移&#xff0c;最终两指针重合则返回true。 期间任一条件不满足则返回false。 代码如下&#xff1a; class Solution { p…

大数据时代下的数据安全防护

随着大数据时代的来临&#xff0c;数据安全防护成为了一个重要的问题。在大数据时代&#xff0c;数据的规模和价值都得到了极大的提升&#xff0c;因此数据安全的重要性也变得越来越突出。本文将从数据加密、访问控制、网络安全和人员管理四个方面来介绍大数据时代下的数据安全…

什么是盒子模型

什么是盒子模型 盒子模型&#xff0c;也可以称为框模型。 所有 HTML 元素可以看作盒子。在 CSS 中&#xff0c;“box model” 这一术语是用来设计和布局时使用。 CSS 盒模型本质上是一个盒子&#xff0c;封装周围的 HTML 元素&#xff0c;它包括&#xff1a;边距&#xff0c…

8. 损失函数与反向传播

8.1 损失函数 ① Loss损失函数一方面计算实际输出和目标之间的差距。 ② Loss损失函数另一方面为我们更新输出提供一定的依据。 8.2 L1loss损失函数 ① L1loss数学公式如下图所示&#xff0c;例子如下下图所示。 import torch from torch.nn import L1Loss inputs torch.tens…

Django静态文件媒体文件文件上传

文章目录 一、静态文件和媒体文件1.在django中使用静态文件实践2.在django中使用媒体文件 二、文件上传单文件上传实践多文件上传 一、静态文件和媒体文件 媒体文件: 用户上传的文件&#xff0c;叫做media 静态文件:存放在服务器的css,js,image,font等 叫做static1.在django中…

【Locomotor运动模块】瞬移

文章目录 一、原理二、两种类型1、Instant(立刻)2、Dash&#xff08;猛冲&#xff09; 三、瞬移区域、瞬移点1、瞬移区域2、瞬移点 一、原理 抛物线指针选择好目标位置&#xff0c;然后告诉瞬移预设体&#xff1a;你想法把游戏区域弄到目标位置来 解释&#xff1a;抛物线指针选…

JS中的new操作符

文章目录 JS中的new操作符一、什么是new&#xff1f;二、new经历了什么过程&#xff1f;三、new的过程分析四、总结 JS中的new操作符 参考&#xff1a;https://www.cnblogs.com/buildnewhomeland/p/12797537.html 一、什么是new&#xff1f; 在JS中&#xff0c;new的作用是通过…