LeetCode 416. 分割等和子集

Java实现 LeetCode 416 分割等和子集 416. 分割等和子集 给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 注意: 每个数组中的元素不会超过 100 数组的大小不会超过 200 示例 1: 输入: [1, 5, 11, 5] 输出: true 解释: 数组可以分割成 [1, 5, 5] 和 [11]. 示例 2: 输入: [1, 2, 3, 5] 输出: false 解释: 数组不能分割... 阅读详情

题目描述

给你一个只包含正整数的非空数组 nums,判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

例如:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1,5,5] 和 [11]

最初思路

这道题一开始可以抽象成一个“选或不选”的问题:

每个 nums[i] 有两种选择:
1. 选它,让目标和减少 nums[i]
2. 不选它,目标和不变

如果数组总和是 sum,想分成两个和相等的子集,本质上就是判断能不能选出一个子集,使它的和等于 sum / 2。

所以最初想到的是 DFS:从下标 0 开始,每个数都分成“选 / 不选”两个分支。

问题出在哪里

第一版 DFS 里有两个容易忽略的点。

第一个是把 res 当成参数传进递归:

dfs(nums, target, 0, res)

但 boolean 是值传递,递归内部即使执行了 res = true,也不会改变 canPartition 里的那个 res。

第二个是边界条件:当 i == nums.length 时,数组已经没有当前数字了。如果这时继续访问 nums[i],就会越界。

修正 DFS 后,还会遇到超时,因为纯 DFS 会反复搜索同一个状态。

例如:

dfs(nums, 10, 8)

可能从很多条不同的选择路径走到这个状态,但它后面的搜索结果其实是一样的。

正确思路

这道题可以看成 0/1 背包问题:

每个数字只能使用一次,判断是否能凑出 target = sum / 2。

如果 sum 是奇数,两个子集不可能平分,直接返回 false。

如果 sum 是偶数,就令:

target = sum / 2

接下来只需要判断能不能从 nums 中选出一些数字,使它们的和刚好等于 target。

重复子问题

如果沿着 DFS 的方向继续优化,可以定义状态:

dfs(i, t)

含义是:从第 i 个数字开始选,还需要凑出和 t,最终能不能成功。

这个状态只和 i 与 t 有关,和前面具体怎么选出来没有关系,所以可以用记忆化避免重复搜索。

进一步改成自底向上的动态规划后,可以直接用一维数组保存状态。

状态定义

定义:

d[j] 表示:在已经处理过的数字中,能不能凑出和 j。

例如:

d[0] = true

表示什么都不选时,可以凑出和 0。

最终答案就是:

d[target]

状态转移

处理当前数字 x 时,对于目标和 j,有两种情况:

1. 不选 x:d[j] 保持原来的值
2. 选 x:如果之前能凑出 j - x,那么现在就能凑出 j

所以状态转移是:

d[j] = d[j] || d[j - x]

其中:

d[j]       -> 不选当前 x
d[j - x]   -> 选当前 x

初始化与遍历顺序

初始化:

d[0] = true

因为不选任何数时,和为 0 一定可以凑出来。

内层循环必须倒序:

for i from target down to x

原因是这道题每个数字只能用一次。倒序更新可以保证 d[i - x] 还是上一轮数字处理完后的状态,不会把当前这个 x 重复使用。

如果正序遍历,问题会变成类似完全背包。

例如:

target = 2
d[0] = true
当前 x = 1

如果正序更新:

i = 1: d[1] = d[1] || d[0]  -> d[1] 变 true
i = 2: d[2] = d[2] || d[1]  -> 这里的 d[1] 是刚刚用同一个 x 更新出来的

这样相当于一个 1 在同一轮里被用了两次。

伪代码

下面的伪代码与后面的 Java 实现一一对应:

计算 nums 的总和 sum
如果 sum 是奇数:
    返回 false

target = sum / 2
创建布尔数组 d,长度为 target + 1
d[0] = true

对于 nums 中的每个数字 x:
    从 target 倒序遍历到 x:
        d[i] = d[i] 或 d[i - x]

返回 d[target]

Java 代码

class Solution {
    public boolean canPartition(int[] nums) {
        int s = 0;
        for (int i = 0; i < nums.length; i++) {
            s += nums[i];
        }

        if (s % 2 != 0) {
            return false;
        }

        int t = s / 2;
        boolean[] d = new boolean[t + 1];
        d[0] = true;

        for (int x : nums) {
            for (int i = t; i >= x; i--) {
                d[i] = d[i] || d[i - x];
            }
        }

        return d[t];
    }
}

易错点

1. 递归结果不能靠普通 boolean 参数带回

如果 DFS 中写:

res = true

这只会改变当前递归栈里的局部变量,不会改变外层的 res。

更合适的写法是让 dfs 返回 boolean,由返回值把“是否找到答案”带回上一层。

2. DFS 要先处理数组越界边界

当 i == nums.length 时,说明所有数字都已经处理完了。如果 t 还不是 0,就应该返回失败,不能继续访问 nums[i]。

边界可以这样理解:

t == 0              -> 已经凑出目标,返回 true
t < 0               -> 超过目标,返回 false
i == nums.length    -> 数字用完,返回 false

3. 记忆化要区分“没算过”和“算过为 false”

如果用记忆化 DFS,状态是 (i, t)。

boolean[][] 只能表示 true / false,不方便表示“这个状态还没算过”。可以用 Boolean[][] memo,其中:

null  -> 没算过
true  -> 这个状态能成功
false -> 这个状态不能成功

4. 一维 0/1 背包必须倒序遍历

当前数字 x 只能使用一次,所以更新 d[i] 时,d[i - x] 应该是处理当前数字之前的状态。

倒序遍历可以做到这一点:

for i from target down to x

正序遍历会让当前数字在同一轮里被重复使用。

建议测试用例

nums = [1,5,11,5]
输出:true

nums = [1,2,3,5]
输出:false

nums = [1,1]
输出:true

nums = [1,2,5]
输出:false

复杂度分析

  • 时间复杂度:O(n * target),其中 target = sum / 2。
  • 空间复杂度:O(target),使用了一维布尔数组保存可达状态。

总结

这道题的关键是把“能不能分成两个和相等的子集”转成“能不能凑出 sum / 2”。

最开始用 DFS 的“选或不选”方向是对的,但需要注意递归返回值、数组边界和重复状态。

最终的一维动态规划写法中,最重要的是理解:

d[j] = d[j] || d[j - x]

以及为什么内层循环必须倒序。倒序不是为了防止下标越界,而是为了防止同一个数字在同一轮里被重复使用。

NHANES更新数据后,权重计算方式也更新了 官网说明在此:如果你比我还懒,我下载了几个说明文件。2021年8月至2023年8月的总样本量与之前的2年周期相似。然而,。特别是,与之前包括过度抽样的周期相比,非西班牙裔白人的样本量较高,而非西班牙裔黑人的样本量较低。此外,60岁及以上人群的样本量较高,而年轻群体的样本量较低。子组的估计值可能具有较低的精确度,因此,检测组间差异的统计能力可能较低。此外,年龄和种族/西班牙裔亚组(尤其是儿童和青少年年龄组)的估计值可能会显示意想不到的模式。 阅读详情

相关推荐

ARM的CI-700和Arteris的NoC对比

设计理念:CI-700注重于为移动和客户端SoC提供定制化的互连解决方案,而Arteris的NoC更侧重于提供灵活、可配置的网络互连技术,适用于多种应用场景。性能与功耗:CI-700通过系统级缓存和网状拓扑结构优化性能和功耗,而Arteris的NoC通过物理感知和多协议支持来提升性能和降低功耗。功能安全:Arteris的NoC提供了功能安全支持,这对于汽车和工业应用尤为重要,而CI-700虽然也关注安全性,但未明确提及功能安全支持。互操作性。

ygyglg的专栏 2622

416. 分割等和子集(力扣LeetCode)

因为力扣提前执行的时候是不会重复加载全局变量的 只会加载一次 之后每次运行新的测试用例只会执行那个功能函数,如需使用,需要每次在函数中使用。

m0_73841621的博客 801

langchain4j+milvus实战

本文主要研究一下如何使用langchain4j来对接向量数据库milvus。

go4it 1472

leetcode 416:分割等和子集

leetcode 416:分割等和子集 题目描述: 给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 输入: [1, 5, 11, 5] 输出: true 解释: 数组可以分割成 [1, 5, 5] 和 [11]. 解题步骤:需要所选则子集之和等于总和一半即可。 1、 状态定义:dp[i][j]表示从数组的 [0, i] 这个子区间内挑选一些正整数,每个...

张毅的博客 718

Zemax操作16--激光谐振腔(含晶体)

谐振腔(含晶体)

lzy750750的博客 8314

【MagicDrive环境配置】新手配俩星期版

1.创建一个新的环境conda create -n newdrive python==3.82.激活该环境conda activate newdrive3.下载MagicDrive源码 git clone --recursive https://github.com/cure-lab/MagicDrive.git,如果出现时间超时八成是网的问题,直接自己下载解压就好3.我的cuda版本是11.7,但11.7导致后续安装有冲突,因此进入。

m0_46261292的博客 2203

Leetcode416. 分割等和子集

给你一个 只包含正整数 的 非空 数组 nums。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。下标范围内选取若干个正整数(可以是 0 个),是否存在一种选取方案使得被选取的正整数的和等于。在定义状态之后,需要考虑边界情况。以下两种情况都属于边界情况。,可以选取也可以不选取,两种情况只要有一个为。需要分别考虑以下两种情况。的情况下无法选取当前的数字。,则在选取的数字的和等于。...

会流泪de鱼的博客 731

UE4载具性能调优指南:从参数设置到实战优化

本文详细解析了UE4载具性能调优的关键技巧,从WheeledVehicle类构建到轮子参数、传动系统设置,再到物理属性与操控调校。通过实战案例分享,帮助开发者优化载具的速度、加速度等核心性能,提升游戏体验。特别适合需要精细调校载具物理特性的开发者参考。

weixin_29019241的博客 469

golang力扣leetcode 416.分割等和子集

416.分割等和子集416.分割等和子集题解代码 416.分割等和子集 416.分割等和子集 题解 题目:给定一个数组,问能否将该数组分成两个数组,并且这两个数组元素累加值一样 思路: 首先剪枝: 1. 数组元素少于2,肯定不行 2. 数组元素累加和是奇数,肯定不行 3. 元素中最大元素>累加和的一半,肯定不行 那么对于数组来说,只要拼凑出元素和=累加和/2,就行了,一定会存在没有选的元素累加=一半的。 1. 刚开始,想的是递归,只有选和不选,超时 2. 记忆化, AC,但是效率很低 对于自上而下的

清风 601

告别手动复制粘贴!用Python脚本+pywinauto实现PC微信消息自动发送(附完整源码)

本文详细介绍了如何利用Python和pywinauto实现PC微信消息的自动化发送,告别繁琐的手动操作。从环境搭建、UI控件定位到构建健壮的发送流程,再到高级功能扩展和项目打包部署,全面解析了自动化办公的实现方案,显著提升工作效率。

weixin_30315723的博客 583

LVGL hal disp(porting display)

LVGL本身是没有对接最终的显示框架的,所以到具体的某个平台上,需要自行porting。

Bigan(安)的博客 2097

leetcode 416. 分割等和子集

0-1背包 // W 为背包总体积 // N 为物品数量 // weights 数组存储 N 个物品的重量 // values 数组存储 N 个物品的价值 public int knapsack(int W, int N, int[] weights, int[] values) { int[][] dp = new int[N + 1][W + 1]; for (int i = 1; i <= N; i++) { int w = weights[i - 1], v

诗和远方,代码和你 200

3、流程引擎下载安装与部署

camunda有多种使用方式,此处先使用独立平台的方式部署引擎,这种用法适合分布式,微服务化,多语言异构系统的使用方式。

leyoliu的博客 5520

【Leetcode hot 100】416.分割等和子集

摘要: 题目要求判断能否将正整数数组分为两个等和子集。解法核心是将问题转化为判断是否存在子集和等于总和一半。通过动态规划,定义 dp[j] 表示能否组成和为 j 的子集,并采用反向遍历避免重复计数。关键剪枝包括:总和为奇数直接返回 false,以及最大元素超过目标和时排除。最终检查 dp[target] 即可得到答案。该算法时间复杂度为 O(n×target),空间复杂度为 O(target)。

专注Java和MySQL领域知识分享 1103

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

【leetcode】—416.分割等和子集

✏️ 关于专栏:专栏用于记录中做题与总结。

Wneosy的博客 914

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

力扣416-分割等和子集(Java详细题解)

01背包是有n个物品,每个物品有其对应的价值和重量,给你一个容量为m的背包,让你求该背包所能放下物品的最大价值。其实这个子集的元素和就可以当做01背包的背包容量,元素本身可以当做他的价值和重量,即他的价值和重量都一致。如果一个子集的元素和等于整个数组的元素和的一半,那么这个子集就可以确定下来,整个数组就可以分为俩个子集。分成俩个子集后,如果我们确定了一个子集的元素和为整个数组和的一半,另一半的就可以确定下来了。由此可见,如果一个数组他的元素和为偶数的话,就可以使俩个子集的元素和相等。

2302_79761426的博客 1212

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

LeetCode 416 分割等和子集

LeetCode 416 分割等和子集 题目链接 给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 注意: 每个数组中的元素不会超过 100 数组的大小不会超过 200 示例 1: 输入: [1, 5, 11, 5] 输出: true 解释: 数组可以分割成 [1, 5, 5] 和 [11]. 示例 2: 输入: [1, 2, 3, 5] 输出: false 解释: 数组不能分割成两个元素和相等的子集. 典型的背包问题~ 首先和为奇数时肯定不能平均分,对和

旺崽的博客 2817

[LeetCode] 416、分割等和子集

题目描述 给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。(不要求连续) 示例: 输入: [1, 5, 11, 5] 输出: true 解释: 数组可以分割成 [1, 5, 5] 和 [11]. 解题思路 看大佬题解,这又是一个01背包的变形。 问题翻译: 给定一个只包含正整数的非空数组。是否可以从这个数组中挑选出一些正整数,每个数只能用一次,使...

aift的专栏 426

leetcode 416:分割等和子集

leetcode 416:分割等和子集 416. 分割等和子集 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 示例 1: 输入:nums = [1,5,11,5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11] 。 示例 2: 输入:nums = [1,2,3,5] 输出:false 解释:数组不能分割成两个元素和相等的子集。 提示: 1 <= nums.length <= 200 1 &l

yingmu__的博客 1207

【Leetcode 每日一题】416. 分割等和子集

Leetcode 每日一题 - 分割等和子集

冠位优化 1200

java分割等和子集(力扣Leetcode416)

我们定义一个二维的动态规划数组dp,其中dp[i][j]表示在前i个物品中,能否选取一些物品使得它们的总和等于j。通过以上步骤,我们可以分析出解决该问题的关键步骤,并用动态规划的思想进行解决。首先计算数组的总和,然后判断是否为偶数,如果不是偶数则返回false。接着根据动态规划的思想初始化dp数组,然后按照状态转移方程进行状态转移,最终返回dp数组的最后一个值。

m0_73065928的博客 650

动态规划LeetCode-416.分割等和子集

本题如何转换到01背包问题是关键,我们想一想,题目说分割两个等和子集,那只需要是sum/2得到一个子集的体积,这个sum/2得到的相当于就是一个背包,这个背包体积是sum/2,看nums里面能否把这个背包体积装满,如果能装满,即可以分割等和子集。初始化:背包容量为j=0,物品最大价值为dp[0]=0这个好理解,那其他下标初始化也为0是为什么呢,因为dp数组在递推的过程中取得最大的价值,把下标初始成负无穷小,就不会被初始值覆盖,这里初始为0即可,也是一样的。数组不能分割成两个元素和相等的子集。

2303_80070412的博客 908

【力扣】416.分割等和子集

力扣 416题 分割等和子集 c++解法

Father_Of_Soft的博客 479

leetcode hot100【Leetcode 416.分割等和子集】java实现

Leetcode 416.分割等和子集-动态规划

DataCrafter的博客 921

力扣-动态规划-416. 分割等和子集

力扣-动态规划-416. 分割等和子集

qianxunsiyue的博客 656

LeetCode 416.分割等和子集 【超详细解答】

LeetCode 416.分割等和子集1. 问题描述2. 解法分析2.1 状态和选择2.2 dp数组的定义2.3 状态转移的逻辑2.4 处理边界情况2.5 初步代码3. 状态压缩/代码优化 1. 问题描述 这是一道经典的0-1背包问题变种,但是更简单一些。首先我们回顾一下背包问题: 给你N个物品和一个可装载重量为W的背包,每个物品有重量和价值两个属性。其中第i个物品的重量为wt[i],价值为val[i],现在让你用这个背包装物品,能装的最大价值是多少? 那么我们这道题如何转换成上述的背包问题呢? N

More than coding 1803
上一篇: LeetCode 22. 括号生成
下一篇: LeetCode 32. 最长有效括号
zander258
博客等级 码龄2年 6粉丝 136原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值