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

【力扣100题】49.分割等和子集

题目描述

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

示例:

  • 输入:nums = [1,5,11,5]→ 输出:true(数组可以分割成[1, 5, 5][11]
  • 输入:nums = [1,2,3,5]→ 输出:false(数组不能分割成两个元素和相等的子集)

解题思路总览

方法思路时间复杂度空间复杂度
动态规划(0-1背包)转化为是否能从数组中选取若干元素使其和等于 sum/2O(n × sum/2)O(sum/2)
DFS + 剪枝从数组中尝试选取元素,看能否凑到目标值O(2^n)O(n)
位运算优化用 bitset 优化空间,适合 sum 较小的情况O(n × sum/32)O(sum/32)

本题采用**动态规划(0-1背包)**方法。


核心思路转化

将原问题转化为:是否可以从数组中选取若干元素,使得它们的和等于 sum/2?

  • 如果能找到一个子集的和为 sum/2,那么剩下的元素和也是 sum/2,问题得解
  • 这就是一个「0-1背包」问题:每个数只能用一次,问能否装满容量为 sum/2 的背包

完整代码

classSolution{public:boolcanPartition(vector<int>&nums){intsum=0;for(inti=0;i<nums.size();i++)sum+=nums[i];if(sum%2==1)returnfalse;vector<int>dp(10001,0);for(inti=0;i<nums.size();i++){for(intj=sum/2;j>=nums[i];j--){dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);}}if(dp[sum/2]==sum/2)returntrue;returnfalse;}};

算法流程图

输入: nums = [1, 5, 11, 5] 计算总和: sum = 1 + 5 + 11 + 5 = 22 sum % 2 == 0? 是 target = sum / 2 = 11 初始化: dp[0...11] = 0 容量为 11 的背包 遍历数组: i = 0, nums[0] = 1: j = 11: j >= 1, dp[11] = max(dp[11], dp[10]+1) = 1 j = 10: j >= 1, dp[10] = max(dp[10], dp[9]+1) = 1 ... j = 1: j >= 1, dp[1] = max(dp[1], dp[0]+1) = 1 dp[1] = 1 i = 1, nums[1] = 5: j = 11: j >= 5, dp[11] = max(dp[11], dp[6]+5) = 6 j = 10: j >= 5, dp[10] = max(dp[10], dp[5]+5) = 6 ... j = 5: j >= 5, dp[5] = max(dp[5], dp[0]+5) = 5 dp[5] = 5 i = 2, nums[2] = 11: j = 11: j >= 11, dp[11] = max(dp[11], dp[0]+11) = 11 dp[11] = 11 i = 3, nums[3] = 5: j = 11: j >= 5, dp[11] = max(dp[11], dp[6]+5) = 11 (不更新) ... 最终 dp[11] = 11 dp[11] == target == 11? 是 返回 true

逐行解析

intsum=0;for(inti=0;i<nums.size();i++)sum+=nums[i];

含义:计算数组所有元素的总和。

if(sum%2==1)returnfalse;

含义:如果总和是奇数,无法平分成两个和相等的子集,直接返回 false。

vector<int>dp(10001,0);

含义:创建背包容量数组。dp[j]表示容量为j的背包最多能装多少重量的物品(元素和)。这里dp大小设为 10001 是因为sum <= 200 × 100 = 20000,所以sum/2 <= 10000

for(inti=0;i<nums.size();i++)

含义:遍历数组中的每个元素(物品)。

for(intj=sum/2;j>=nums[i];j--)

含义:0-1 背包的关键:内层循环倒序遍历背包容量。这样可以保证每个元素只被使用一次(正序会导致完全背包问题)。

dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);

含义:状态转移方程。对于当前元素nums[i],要么不放入背包(保持dp[j]),要么放入背包(dp[j - nums[i]] + nums[i])。取较大值更新。

if(dp[sum/2]==sum/2)returntrue;

含义:遍历结束后,如果dp[sum/2]恰好等于sum/2,说明可以找到一个子集的和为sum/2,返回 true。

returnfalse;

含义:无法找到和为sum/2的子集,返回 false。


为什么内层循环要倒序?

以 nums = [1, 5] 为例,target = 3 正序(错误): i=0, num=1: dp[1] = max(dp[1], dp[0]+1) = 1 dp[2] = max(dp[2], dp[1]+1) = 2 <- 用到了本轮刚更新的 dp[1] dp[3] = max(dp[3], dp[2]+1) = 3 <- 用到了本轮刚更新的 dp[2] 结果:num=1 被使用了多次!变成完全背包了 倒序(正确): i=0, num=1: dp[3] = max(dp[3], dp[2]+1) dp[2] 还是旧值 dp[2] = max(dp[2], dp[1]+1) dp[1] 还是旧值 dp[1] = max(dp[1], dp[0]+1) = 1 结果:num=1 只被使用一次

复杂度分析

复杂度说明
时间复杂度O(n × sum/2)外层循环 n 次,内层循环 sum/2 次
空间复杂度O(sum/2)dp 数组大小为 sum/2 + 1

面试追问 FAQ

问题答案
为什么能转化成 0-1 背包问题?如果能找到和为 sum/2 的子集,剩下的元素和也是 sum/2,所以问题等价于「能否从数组中选若干元素使其和为 sum/2」
dp[j]的含义是什么?容量为 j 的背包最多能装多少重量的物品(即最多能达到多大的和)
为什么不直接用布尔数组dp[j]表示能否凑到 j?因为dp[j]表示「最多」能装多少,可以用来判断是否恰好装满;布尔数组需要额外记录具体方案
dp[sum/2] == sum/2为什么能判断「恰好装满」?dp[sum/2]是背包最多能装到的重量,如果它等于sum/2说明可以恰好装满
进阶:如何输出具体的分割方案?额外记录每个状态的选择路径,从dp[sum/2]开始回溯,找出被选中的元素
进阶:如何优化空间?使用 bitset:`bitset<10001> bits; bits[0] = 1; bits

相关题目

题号题目难度核心思路
416分割等和子集中等0-1 背包
322零钱兑换中等完全背包
279完全平方数中等动态规划
494目标和中等0-1 背包(计数)
1049最后一块石头的重量 II中等0-1 背包

总结

要点内容
核心思想将分割等和子集转化为 0-1 背包:能否从数组中选若干元素使其和为 sum/2
状态定义dp[j]= 容量为 j 的背包最多能装的重量(元素和)
状态转移dp[j] = max(dp[j], dp[j-nums[i]] + nums[i])
关键点内层循环倒序,保证每个元素只使用一次
边界条件sum 为奇数时直接返回 false
结果判断dp[sum/2] == sum/2表示恰好装满

http://www.jsqmd.com/news/828936/

相关文章:

  • 基于Fabric.js的Web视频编辑器:架构、实现与性能优化
  • 终极Android数学计算神器:nCalc深度解析与使用指南
  • 告别‘悬空’和‘穿模’:Cesium地形上精准放置Entity的5个调试技巧与性能考量
  • PDF怎么转JPG图片?2026年PDF转换方法对比与实测指南 - AI测评专家
  • 怎么用工商登记信息判断一家企业的真实主营业务?一份字段解读手册
  • VASP计算进阶:磁性、HSE06、SOC这些参数到底怎么加?一份参数设置避雷手册
  • WebAssembly Python完全指南:浏览器端Python开发终极方案
  • PE-bear:3分钟快速上手,Windows可执行文件逆向分析终极工具
  • LIKWID核心功能解析:CPU性能计数器、拓扑检测与电源监控
  • NHSE完整指南:5步掌握动物森友会存档编辑器的终极使用方法
  • Docker一条命令部署kkFileView?这些隐藏的配置和优化技巧你可能不知道
  • Y CRDT 网络协议完全解析:WebSocket 和 WebRTC 集成实战
  • GeoPattern颜色系统深度剖析:如何智能控制背景色与填充色
  • 图像采集卡系统集成实战:从硬件选型到软件部署的完整指南
  • ElevenLabs孟加拉文TTS部署踩坑大全:Docker容器内字体缺失、Bengali RTL渲染错位、SSML `<break time=“200ms“/>` 失效的5大根源及热修复补丁
  • 合肥名表实体门店深度测评 线下交易细节全面拆解 - 奢侈品回收测评
  • Nintendo Switch大气层系统终极指南:从零开始的安全定制体验
  • HTTPCanary Magisk模块终极指南:轻松突破Android HTTPS抓包限制的完整解决方案
  • 如何在5分钟内开始使用MedSAM进行医学图像分割
  • 在多轮对话应用中实测不同模型通过聚合API调用的响应速度体感
  • LanguageTool Python:5分钟学会为你的应用添加智能语法检查功能 [特殊字符]✅
  • TPT19形式化需求:从自然语言到自动化测试用例的工程实践
  • Citra模拟器终极指南:5分钟快速体验3DS游戏世界
  • AI应用合规实战:开源法律合规助手架构设计与实现
  • 2026广州救护车推荐及非急救转运服务挑选实用指南 - 榜单测评
  • Steam饰品交易分析利器:打造你的专属市场监控系统
  • 内容创作团队如何借助Taotoken聚合能力提升内容生成效率
  • 如何从零打造一台智能六足机器人:完整开源指南
  • spring cloud seata 知识点
  • 【卷卷观察】一条音频文件就能接管你的手机——Pixel 10零点击漏洞链全解析