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

算法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 <= nums[i] <= 100

题解

class Solution:def canPartition(self, nums: List[int]) -> bool:memo={}# dfs的目的是考虑前 i+1 个数(即 nums[0] 到 nums[i])的情况下,能否选出一个子集,使其元素之和恰好等于jdef dfs(i:int,j:int) -> bool:if (i,j) in memo:return memo[(i,j)]# 结束条件,i遍历完if i<0:return j==0# 背包装不下,或者可以理解成所有值得和大于0,所以不存在j-nums[j]<0的情况# 剪枝if j<nums[i]:memo[(i,j)] = dfs(i-1,j)return dfs(i-1,j) # 不选择该值memo[(i,j)] = dfs(i-1,j-nums[i]) or dfs(i-1,j)return dfs(i-1,j-nums[i]) or dfs(i-1,j)s=sum(nums)return s%2==0 and dfs(len(nums) - 1, s // 2)

这里可以使用0-1背包的思路思考这道题

🎯 一、问题转化:从“分割数组”到“0-1 背包”

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

✅ 转化步骤:

  1. 计算总和:s = sum(nums)
  2. 如果 s 是奇数 → 不可能平分 → 直接返回 False
  3. 如果 s 是偶数 → 目标变成:能否从 nums 中选出若干个数,使其和恰好等于 target = s // 2

🔑 这就是经典的 0-1 背包问题中的“恰好装满”判定问题


🎒 二、0-1 背包的标准模型回顾

背包要素本题对应内容
物品数组中的每个数 nums[i]
物品重量nums[i](同时也是“价值”,但这里不关心价值)
背包容量target = s // 2
选择限制每个数最多选一次(0-1)
目标能否恰好装满背包(即子集和 = target)

💡 注意:这里我们不求最大价值,而是判断是否存在一种选法,使总重量恰好等于容量


🧠 三、状态定义(DP / DFS)

递归函数定义(记忆化搜索版):

def dfs(i, j) -> bool:
  • 含义:考虑前 i+1 个物品(即 nums[0..i]),能否选出一个子集,使其和恰好为 j
  • 目标dfs(n-1, target)

这完全对应 0-1 背包的状态定义:dp[i][j] = 能否用前 i 个物品凑出重量 j


🔁 四、状态转移方程(0-1 背包核心)

对于第 i 个物品(重量 = nums[i]),有两种选择:

情况 1️⃣:不选第 i 个物品

  • 那么问题变成:用前 i-1 个物品凑出 j
  • 对应:dfs(i - 1, j)

情况 2️⃣:选第 i 个物品(前提是能装下!)

  • 条件:j >= nums[i]
  • 那么问题变成:用前 i-1 个物品凑出 j - nums[i]
  • 对应:dfs(i - 1, j - nums[i])

✅ 状态转移方程:

if j >= nums[i]:dfs(i, j) = dfs(i-1, j - nums[i]) or dfs(i-1, j)
else:dfs(i, j) = dfs(i-1, j)   # 只能不选

这正是你代码中的逻辑!


🛑 五、边界条件(Base Case)

if i < 0:  # 没有物品可选了return j == 0
  • 当没有物品时(i = -1):
    • 如果目标 j == 0 → 空集满足条件 → True
    • 如果 j > 0 → 无法凑出正数 → False

这对应背包问题中“用 0 个物品凑出 0 重量是可行的,凑出正重量不可行”。


🧪 六、举个完整例子(按背包逻辑走一遍)

nums = [1, 5, 11, 5]

  1. s = 22target = 11
  2. 问:能否从 [1,5,11,5] 中选出若干数,和为 11?
  3. 物品:4 个,重量分别是 1, 5, 11, 5
  4. 背包容量:11

尝试选法:

  • 11 → 刚好凑满 ✅
  • 或选 1 + 5 + 5 = 11

所以 dfs(3, 11) 应返回 True

递归过程会探索:

  • i=3(值为 5)时,j=11 >= 5 → 尝试选或不选
    • 选:看 dfs(2, 6)
    • 不选:看 dfs(2, 11)
  • dfs(2, 11) 中,nums[2]=11j=11 >= 11 → 选它 → dfs(1, 0) → 最终 i=-1, j=0True

🧩 七、为什么这是“0-1 背包”而不是“完全背包”?

  • 0-1 背包:每个物品只能用一次 → 对应“每个数只能选一次”
  • 完全背包:物品可用无限次 → 不符合题意(不能重复使用同一个元素)

所以本题是标准的 0-1 背包判定问题


📌 八、总结:0-1 背包视角下的完整逻辑链

步骤内容
1. 问题转化分割等和子集 ⇨ 能否选出子集和 = sum(nums)//2
2. 模型识别物品 = 数字,重量 = 数值,容量 = target,选或不选 → 0-1 背包
3. 状态定义dfs(i, j) = 前 i+1 个数能否凑出和 j
4. 状态转移能装下就“选或不选”,装不下就“只能不选”
5. 边界条件无物品时,只有 j=0 可行
6. 最终答案dfs(n-1, target)

结论
canPartition 问题本质上就是一个 0-1 背包的“可行性判定”变种,完全符合 0-1 背包的逻辑框架。

代码正是用记忆化搜索实现的 0-1 背包解法,非常标准!

http://www.dtcms.com/a/496177.html

相关文章:

  • 编织网站建设网站建设考核
  • 2-SpringCloud-Consul服务注册与发现和分布式配置管理
  • 前后端分离项目java+vue 加密一般用几种加密方式,具体是什么加密,怎么用的
  • 从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
  • matlab_学习_均分数据
  • 深圳免费建站山东省建设节能协会网站
  • 青岛万维网站设计珠宝类企业网站(手机端)
  • 【展厅多媒体】触摸查询一体机实现数据可视化
  • linux学习笔记(37)mysql视图详解
  • 自定义配置小程序tabbar逻辑思路
  • 临沂网站制作网站微信商城搭建
  • 月牙河做网站公司搜索引擎排名规则
  • 多字节串口收发IP设计(七)串口接收模块合并及排故(含源码)
  • 建立网站的意义哪个建站平台较好
  • 如何进行坡度分析
  • 做网站源代码网页制作模板
  • VexIR2Vec : An Architecture-Neutral Embedding Framework for Binary Similarity
  • 判断链表是否为回文
  • 知名设计网站公司想学网络运营怎么开始
  • AI产品经理学习笔记4 - Agent的技术框架
  • 中国住房和建设部网站首都之窗官网
  • 淘宝买cdk自己做网站湖北手机网站制作
  • JavaSE面向对象(下)
  • 网站怎么做前台跟后台的接口小说网站推广方式
  • Node.js v25 重磅发布!革新与飞跃:深入探索 JavaScript 运行时的未来
  • 2一、u-boot下载编译
  • C++ MFC控件实现小型通讯录
  • 东莞网站优化一般多少钱深圳网站seo优化公司
  • 免费制作app生成器网站馆陶网站建设电话
  • 从发币到行为经济:BSC 发币工具演化的下一站