文章目录

  • 题目描述
  • 思路 && 代码
    • 1. 动态规划 O(n2n^2n2)、O(n2n^2n2)(最方便理解,初版)
    • 2. 转换成 01 背包问题 O(n2n^2n2)、O(nnn)
    • 二刷
      • 离谱!添加了测试用例,上面的代码需要添加负数条件了(见下面的代码)

打卡第十五天~继续加油!

题目描述

  • 和上一道分割等和子集的做法很像
  • 这里代码迭代了很多次,但是时间复杂度是一样的,只是空间复杂度、耗时不断优化。

思路 && 代码

1. 动态规划 O(n2n^2n2)、O(n2n^2n2)(最方便理解,初版)

  • dp[i][j]:下标[0 ~ i]构成的数集,能得到 j - sum 的情况种数
  • 因为nums[ ]可构成元素范围为[-sum, sum],因此开出[2 * sum + 1]的列数组。
    其中[0] 代表 -sum,[2 * sum] 代表 [sum],以此类推。
  • 注意:初始化时,nums[0] 可能等于 -nums[0],因此要用到 +=,而非 = 。
  • 状态转移:对于当前的 j,分成 +nums[i],和-nums[i]的情况,对上一行的值进行选取即可。
  • 缺陷:空间复杂度不方便通过滚动数组的方式进行优化,因为状态转移方程的过程中,不但用到了前面的元素,还用到的后面的元素。解决方法:转换成01背包问题
class Solution {public int findTargetSumWays(int[] nums, int target) {int sum = 0;for(int temp : nums) {sum += temp;}// 全正 or 全负,不在范围的情况if(sum < target || -sum > target) {return 0;}// dp[i][j]:下标[0 ~ i]构成的数集,能得到 j - sum 的情况种数int top = 2 * sum + 1;int[][] dp = new int[nums.length][top];// 初始化:只取第一个元素,只能给 nums[0] 和 -nums[0] 带来 1 个种数dp[0][sum + nums[0]] = 1;// 注意:存在 nums[0] == -nums[0] 的情况,因此这边要用 +=dp[0][sum - nums[0]] += 1;// 状态转移for(int i = 1; i < nums.length; i++) {for(int j = 0; j < top; j++) {// Case 1:第 i 个元素取 + if(j >= nums[i]) {dp[i][j] = dp[i - 1][j - nums[i]];}// Case 2: 第 i 个元素取 -if(j + nums[i] < top) {dp[i][j] += dp[i - 1][j + nums[i]];}}}return dp[nums.length - 1][target + sum];}
}

2. 转换成 01 背包问题 O(n2n^2n2)、O(nnn)

  • 实际上,题目可以这样转换成01背包问题:把 - 当成 0,不选;把 + 当成 1,选。
  • 原本的(-nums) + (+nums) == target,表达式左边和右边都加上 sum,就转换成
    0 + 2 * (+nums) == sum + target,方便起见,我们可以再进行除2操作,变成
    +nums == (sum + target) / 2
  • 注意:由此可推出,如果(sum + target) 为奇数,说明不存在对应的 +nums 序列,也就是不可取。
  • 接下来就可以正常地进行 01背包 的动态规划了~
  • 滚动数组:逆序,现在没有减法的情况下,可以保证无后效性
class Solution {public int findTargetSumWays(int[] nums, int target) {int sum = 0;for(int temp : nums) {sum += temp;}// 不在范围的情况 && 奇数无法匹配到选取方式(可证)if(sum < target || (sum + target) % 2 == 1) {return 0;}// 转换成 01背包:-号转成不取;+号转成取,两倍// 实际上,只要考虑到 target + sum 即可,后面的和结果无关int top = (sum + target) / 2 + 1;// dp[i][j]:下标[0 ~ i]构成的数集,能得到 j - sum 的情况种数int[] dp = new int[top];// 初始化:第一个元素不取,只能给 0 带来 1 个种数dp[0] = 1;// 状态转移for(int i = 0; i < nums.length; i++) {for(int j = top - 1; j >= nums[i]; j--) {// Case 1:取第 i 个元素dp[j] += dp[j - nums[i]];// Case 2: 不取第 i 个元素(一维情况下相当于不用考虑)}}return dp[top - 1];}
}
  • 来个无注释版代码吧,方便直接看代码:
class Solution {public int findTargetSumWays(int[] nums, int target) {int sum = 0;for(int temp : nums) {sum += temp;}if(sum < target || (sum + target) % 2 == 1) {return 0;}int top = (sum + target) / 2 + 1;int[] dp = new int[top];dp[0] = 1;for(int i = 0; i < nums.length; i++) {for(int j = top - 1; j >= nums[i]; j--) {dp[j] += dp[j - nums[i]];}}return dp[top - 1];}
}

二刷

离谱!添加了测试用例,上面的代码需要添加负数条件了(见下面的代码)
class Solution {public int findTargetSumWays(int[] nums, int target) {// 转换成背包:+取两次,-不取。target 相当于加了一次 sumfor(int temp : nums) {target += temp;}// 偶数之和不能为奇数 || 非负数之和不能为负if(target % 2 == 1 || target < 0) {return 0;}target /= 2;int[] dp = new int[target + 1];// 边界处理:0的组合法有一个(都不取)dp[0] = 1;for(int i = 0; i < nums.length; i++) {for(int j = target; j >= nums[i]; j--) {// 相当于,这一轮的结果 = 上一轮的结果 + 这一轮的添加dp[j] += dp[j - nums[i]];}}return dp[target];}
}
  • 无注释版,11行有效代码
class Solution {public int findTargetSumWays(int[] nums, int target) {for(int temp : nums) {target += temp;}if(target % 2 == 1 || target < 0) {return 0;}target /= 2;int[] dp = new int[target + 1];dp[0] = 1;for(int i = 0; i < nums.length; i++) {for(int j = target; j >= nums[i]; j--) {dp[j] += dp[j - nums[i]];}}return dp[target];}
}

【LeetCode笔记】494. 目标和(Java、动态规划、背包问题、滚动数组)相关推荐

  1. 力扣 三步问题动态规划加滚动数组

    面试题 08.01. 三步问题 三步问题.有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶.2阶或3阶.实现一种方法,计算小孩有多少种上楼梯的方式.结果可能很大,你需要对结果模100000000 ...

  2. 力扣198.打家劫舍---动态规划与滚动数组

    力扣198.打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动 ...

  3. 494. 目标和 golang 动态规划

    题目 494. 目标和 给定一个非负整数数组,a1, a2, -, an, 和一个目标数,S.现在你有两个符号 + 和 -.对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面. ...

  4. 动态规划0-1背包问题滚动数组

    1.经典0-1背包问题 问题描述:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内选择物品使得物品的总价值最高. 回顾对于二维的0-1背包问题递推关系式: d p [ i ] [ j ] ...

  5. leetcode 494. Target Sum | 494. 目标和(动态规划)

    题目 https://leetcode.com/problems/target-sum/ 题解 经典 dp,直接看草稿: class Solution {public int findTargetSu ...

  6. 【LeetCode笔记】560. 和为K的子数组(Java、前缀和、哈希表)

    文章目录 题目描述 思路 & 代码 暴力法 O(n2n^2n2) 前缀和 + 哈希表 O(n) 二刷 题目描述 第一道前缀和题目- 思路 & 代码 暴力法 O(n2n^2n2) 固定一 ...

  7. 动态规划 0-1背包问题 滚动数组

    定义 dp[j]是从物品0到i中挑选物品,放进容量为j的背包中的最大价值总和. 初始化 int dp[maxn]; memset(dp, sizeof(dp), -0x3f3f3f3f); 一维滚动数 ...

  8. 代码随想录算法训练营第42天 | 动态规划 part04 ● 背包问题二维● 背包问题滚动数组 一维 ● 416. 分割等和子集

    # 二维dp数组,01背包 1.确定dp数组以及下标的含义 dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少. 2.  gpt 解决我的困惑 3.  另外 ...

  9. LeetCode刷题笔记 动态规划 背包问题

    ​ 背包问题:有 N 个物品和容量为 W 的背包,每个物品都有自己的体积 w 和价值 v,求拿哪些物品可以使得背包所装下物品的总价值最大.如果限定每种物品只能选择 0 个或 1 个,则问题称为 0-1 ...

  10. 01背包问题(当有的背包重量是非整数时)的递归(优化成动态规划+再用滚动数组优化)解法+一些动态规划(递归,搜索)的高级技巧

    当背包重量是整数时,动态规划可以用数组存储状态就可以了. 当背包重量是非整数时,用map存储状态就可以了!主要思路:  map(构造函数中参数comp是仿函数(或者叫函数对象))+递归优化(" ...

最新文章

  1. 几十亿打水漂!世界最大移动通信展MWC因疫情33年来首次取消,多方损失惨重...
  2. CocoaPods私有库创建
  3. iOS内存管理知识点
  4. 设计模式(十)享元模式Flyweight(结构型)
  5. Linux 下安装与卸载JDK
  6. sqlmap (--os-shell)的使用
  7. shell设计精髓_交互设计精髓
  8. 养心灵,才能美容颜,拥有好日子(图)
  9. java中如何声明外键约束,外键约束不正确 - java-mysql
  10. rfid阅读器的主要任务_RFID阅读器(读写器)的应用领域及其如何使用?
  11. 北上广深杭程序员买房姿势图鉴
  12. Test SRM Level Two: CountExpressions, Brute Force
  13. java怎么对用户做自定义模版打印_Printing tools 自定义模板打印的实现
  14. PHP 验证日期格式
  15. 《CCNA学习指南:数据中心(640-911)》——2.5 考试要点
  16. vba 抓取php网页,用VBA操作网页并抓取数据
  17. kali-扫描主机-Nmap
  18. 广电linux机顶盒装软件,电视盒子如何安装第三方软件(四种参考方法)
  19. 如何制作优秀的PPT
  20. 37.WLAN Qos介绍

热门文章

  1. 总结下载Spring Framework的jar包
  2. JAVA编写提示用户输入投资额_java(计算银行存款总额(要求输入错误时,提示重新输入))...
  3. python3.6配置环境变量_python安裝及环境变量配置
  4. 使用Hadoop自带的例子pi计算圆周率
  5. 网络协议及IP地址分类
  6. JavaSE——Java介绍与环境变量简述
  7. openMP的简单使用
  8. 两层卷积网络实现手写数字的识别(基于tensorflow)
  9. 利用Inception-V3训练的权重微调,实现猫狗分类(基于keras)
  10. 数字图像处理实验四图像频域增强