当前位置: 首页 > news >正文

LeetCode 01背包 494. 目标和

494. 目标和

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


题解

首先对题目进行分析
所有的数字都要选,我们能做的是决定数字前面的符号,也就是这个数字是 + 还是 -
那么不妨将最后的结果视为 p - a,p为正数之和,a为负数之和,我们能够通过选择数字决定 p 是多少
又因为 p - a = target,a = sum(所有数的绝对值之和)- p,所以 p = (target + sum)/2
题目就变成从数组 nums 中选择数字使其和为 (target + sum)/2 (不能重复选择) ,这样就是01背包问题

由于 p 是非负整数,所以如果target + sum是奇数或者负数,那么答案肯定是 0

定义 arr[ i ][ j ] 为选择前 i 个数使其和为 j 的方法数
那么对于任意 arr[ i ][ j ] ,只有选择 nums[ i-1 ] 和不选择两种可能
如果 nums[ i-1 ]>j,只能不选,arr[ i ][ j ]=arr[ i-1 ][ j ]
否则,arr[ i ][ j ]=arr[ i-1 ][ j ] + arr[ i-1 ][ j-nums[ i-1] ]
初始化 arr[0][0]=1,其余arr[0][j]为0
按顺序遍历数组即可


代码如下↓

class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {// p p是我们选择的正数// s-p s是所有正数的和// 2p-s=target// p=(target+s)/2// 那么就是选择数使其和为 p 即可int n=nums.size();int s=0;for(int i=0;i<n;i++){s+=nums[i];}if((target+s)%2 || target+s<0){return 0;}int p=(target+s)/2;vector<vector<int>> arr(n+1,vector<int>(p+1,0));arr[0][0]=1;for(int i=1;i<=n;i++){for(int j=0;j<=p;j++){arr[i][j]=arr[i-1][j];if(nums[i-1]<=j){arr[i][j]+=arr[i-1][j-nums[i-1]];}}}return arr[n][p];}
};

优化空间

二维滚动数组

我们发现 arr[ i ][ j ]=arr[ i-1 ][ j ] + arr[ i-1 ][ j-nums[ i-1] ] 或 arr[ i ][ j ]=arr[ i-1 ][ j ]
arr[ i ][ j ] 仅与上一行的 arr 有关,那我们不妨将二维数组缩减至两行,一行存 i-1 ,一行存 i
在执行过程中进行滚动,将 i 和 i-1变为 i%2 和 (i-1)%2,从而优化空间


代码如下↓

class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {// p p是我们选择的正数// s-p s是所有正数的和// 2p-s=target// p=(target+s)/2// 那么就是选择数使其和为 p 即可int n=nums.size();int s=0;for(int i=0;i<n;i++){s+=nums[i];}if((target+s)%2 || target+s<0){return 0;}int p=(target+s)/2;vector<vector<int>> arr(2,vector<int>(p+1,0));arr[0][0]=1;for(int i=1;i<=n;i++){for(int j=0;j<=p;j++){arr[i%2][j]=arr[(i-1)%2][j];if(nums[i-1]<=j){arr[i%2][j]+=arr[(i-1)%2][j-nums[i-1]];}}}return arr[n%2][p];}
};

一维数组

类似的,既然arr[ i ][ j ] 仅与上一行的 arr 有关,一行,我们能否用一位数组表示

arr[ j ] = arr[ j ] + arr[ j-nums[ i-1 ] ](前面的arr[ j ]是arr[ i ][ j ],后面的都是arr[ i-1 ][ j ],上一行的)

同时,我们发现 arr[ j+1 ] 与其上一行的前面的数据有关
如果我们从前往后进行遍历,那么后面的 arr[ j+1 ] 需要的 arr[ j ] 的数据就被新计算出来的 arr[ j+1 ] 给覆盖了
所以我们需要从后向前遍历,这样就不会发生需要的数据被覆盖的问题了


代码如下↓

class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {// p p是我们选择的正数// s-p s是所有正数的和// 2p-s=target// p=(target+s)/2// 那么就是选择数使其和为 p 即可int n=nums.size();int s=0;for(int i=0;i<n;i++){s+=nums[i];}if((target+s)%2 || target+s<0){return 0;}int p=(target+s)/2;vector<int> arr(p+1);arr[0]=1;for(int i=1;i<=n;i++){for(int j=p;j>=0;j--){if(nums[i-1]<=j){arr[j]+=arr[j-nums[i-1]];}}}return arr[p];}
};
http://www.dtcms.com/a/357718.html

相关文章:

  • 顶点 (VS)vs 片段(FS):OpenGL纹理滚动着色器的性能博弈与设计哲学
  • Java进阶教程之多线程与并发编程
  • Windows下快速配置UDF编译环境的详细步骤
  • VexCL并行异构库介绍和使用
  • Python Imaging Library (PIL) 全面指南:PIL图像处理异常处理与优化
  • oceanbase-参数及变量的记录
  • LeetCode 刷题【56. 合并区间】
  • 新人桌球笔记
  • Apisix工作流程
  • 主流国产数据库:文档完备性
  • 进程与线程的根本区别
  • 【双指针 - LeetCode】42. 接雨水
  • gstreamer使用hook的简单示例
  • 用户自定义字段(Custom Fields)设计方案,兼顾多语言、分组、校验、权限、查询性能、审计与多租户
  • LeetCode - 128. 最长连续序列
  • LeetCode第二题知识点3 ----引用类型
  • lxml库如何使用
  • DSP280049 CLA可访问资源
  • 【开题答辩全过程】以 非遗信息管理系统为例,包含答辩的问题和答案
  • 2025年企业管理与经济、文化发展国际会议(MECD 2025)
  • 拎包入住搭建 Browser Use Agent:基于PPIO Model API +Agent 沙箱的一体化构建
  • React-Native项目回忆
  • QML Chart组件之坐标轴共有属性
  • 基于Springboot + vue3实现的教育资源共享平台
  • Java流程控制03——顺序结构(本文为个人学习笔记,内容整理自哔哩哔哩UP主【遇见狂神说】的公开课程。 > 所有知识点归属原作者,仅作非商业用途分享)
  • PCIe 6.0 TLP路由机制:解密高效数据传输的核心架构
  • 贪心算法面试常见问题分类解析
  • 了解 JavaScript 虚拟机(VM)引擎
  • 【项目思维】编程思维学习路线(推荐)
  • Simulink过程数据存储为mat