【Hot 100 刷题计划】 LeetCode 416. 分割等和子集 | C++ 0-1背包 1D空间极致优化

LeetCode 416. 分割等和子集

📌 题目描述

题目级别:中等

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

  • 示例 1:

    输入:nums = [1,5,11,5]

    输出:true

    解释:数组可以分割成 [1, 5, 5][11]

  • 示例 2:

    输入:nums = [1,2,3,5]

    输出:false

    解释:数组不能分割成两个元素和相等的子集。


💡 破题思路:转化为 0-1 背包问题

这道题表面上是"分割数组",但稍加数学转化就能看穿它的本质:

如果数组能够被平分成和相等的两份,那么整个数组的总和 sum 必须是偶数 ,且我们要在数组中挑出一部分数字,使得它们的和恰好等于 sum / 2

这就完美契合了 0-1 背包问题 的模型:

  • 背包总容量target = sum / 2
  • 物品 :数组中的每个数字 nums[i]
  • 物品重量:数字本身的大小。
  • 物品价值:依然是数字本身的大小(本题只需关心能否装满,不需要价值最大化,布尔值即可)。
  • 核心限制:每个数字只能使用一次(0-1 背包)。

本解法高光点(状态压缩与倒序遍历):

传统的二维背包需要 dp[i][j] 记录前 i 个物品凑成重量 j 的状态。

但实际上,我们在计算第 i 行时,仅仅依赖于第 i-1 行的数据。因此,我们可以直接把二维数组压缩成一维数组 dp[j]
⚠️ 致命细节 :在一维数组中,内层循环遍历容量 j 时,必须从大到小倒序遍历! 如果正序遍历,我们在计算 dp[j] 时,用到的 dp[j - nums[i]] 可能在这一轮已经被当前物品更新过了,导致一个物品被重复放入(那就变成完全背包了)。倒序遍历完美保证了每个物品只被放入一次。


💻 C++ 代码实现 (满分标准模板)

cpp 复制代码
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        int sum = 0, ma = 0;
        
        // 1. 计算总和并寻找最大元素
        for (int i = 0; i < n; i++) {
            sum += nums[i];
            ma = max(ma, nums[i]);
        }

        // 2. 极致剪枝 (大厂面试加分项)
        // 如果总和是奇数,绝对不可能平分
        if (sum % 2 != 0) return false;
        
        int target = sum / 2;
        
        // 如果数组中最大的元素直接比目标和还要大,那肯定凑不出来
        if (ma > target) return false;

        // 3. 一维 DP 数组
        // dp[j] 表示能否从数组中挑选出和为 j 的子集
        vector<bool> dp(target + 1, false);
        
        // 核心起跑点:什么都不挑,凑出和为 0 永远是 True
        dp[0] = true;

        // 外层循环:遍历每一个数字 (物品)
        for (int i = 0; i < n; i++) {
            // 内层循环:遍历背包容量。必须【倒序】,防止数字被重复使用!
            // 优化:j 只需要遍历到 nums[i],因为比 nums[i] 还小的容量根本装不下当前物品
            for (int j = target; j >= nums[i]; j--) {
                // 状态转移:能否凑出 j,取决于原来就能凑出 j,或者能凑出 (j - 当前数字)
                dp[j] = dp[j] || dp[j - nums[i]];
            }
        }

        return dp[target];
    }
};
相关推荐
2401_872418787 小时前
算法入门:数据结构-堆
数据结构·算法
王老师青少年编程8 小时前
信奥赛C++提高组csp-s之搜索进阶(搜索剪枝案例实践1)
c++·csp·高频考点·信奥赛·提高组·搜索剪枝·小木棍
xwz小王子9 小时前
手术机器人登上Science Robotics:2毫米纤细手臂,从3厘米切口完成腰椎神经减压
算法·机器人
黎阳之光10 小时前
视频孪生智护供水生命线:黎阳之光赋能医疗与园区水务高质量升级
运维·物联网·算法·安全·数字孪生
Black蜡笔小新10 小时前
自动化AI算法训练服务器DLTM制造业AI质检工作站助力制造业实现AI智检
人工智能·算法·自动化
嵌入式小能手10 小时前
飞凌嵌入式ElfBoard-进程间的通信之命名管道
linux·服务器·算法
啦哈拉哈11 小时前
Leetcode题解记录-hot100(81-100)
算法·leetcode·职场和发展
csdn_aspnet11 小时前
Java 霍尔分区算法(Hoare‘s Partition Algorithm)
java·开发语言·算法
王老师青少年编程11 小时前
信奥赛C++提高组csp-s之搜索进阶(搜索剪枝核心思想 )
c++·dfs·csp·信奥赛·搜索剪枝·搜索优化
一拳一个呆瓜11 小时前
【STL】使用 C++ 标准库标头
c++·stl