【力扣-背包】1、分割等和子集(416)

149 阅读2分钟

「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。

416. 分割等和子集

题目描述

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

示例 1:

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

示例 2:

输入: nums = [1,2,3,5]
输出: false
解释: 数组不能分割成两个元素和相等的子集。

解析

/**
 * @brief 01背包问题
 * 动态规划:
 * 1、确定dp数组以及下标的含义
 *      01背包中,dp[i]表示:容量为j的背包,可以装下物品的总价值最大为 dp[i]
 *      本题中:dp[i]表示:背包总容量为 i , 最大可以凑成i的子集总和为 dp[i]
 * 2、确定递推公式
 *      01背包中:dp[j] = max(dp[j],dp[j-weight[i]]+value[i]);
 *      本题中:dp[j] = max(dp[j],dp[j-nums[i]]+nums[i]);
 *              相当于背包里放入数值,物品 i 的重量为nums[i] , 价值也是 nums[i]
 * 3、数组初始化
 *      全部初始化为0
 *      题述中,每个元素值不超100,数组元素个数不超200,总和则不超 20000
 *      所以背包最大只需要其中一半,所以元素大小为10001即可
 *      vector<int> dp(10001,1);
 * 4、确定遍历顺序
 *      使用一维数组dp时。物品遍历的for循环放在外层,遍历背包的for循环放在内层,
 *      且内层的for循环倒序
 *   
 */

代码

class Solution
{
public:
    bool canPartition(vector<int> &nums)
    {
        // 进行排序
        sort(nums.begin(), nums.end());
        
        int sum = 0;
        // 背包最大值为10001
        vector<int> dp(10001, 0);
        
        for (int i = 0; i < nums.size(); i++)
        {
            sum += nums[i];
        }
        
        // 如果总和为奇数,则不能等分
        if (sum % 2 == 1)
        {
            return false;
        }
        
        // 等分后的背包重量
        int target = sum / 2;

        //01 背包
        // 先进行物品遍历
        for (int i = 0; i < nums.size(); i++)
        {
            // 再遍历背包
            for (int j = target; j >= nums[i]; j--)
            {
                // 背包容量为j时,最大的价值
                // 物品的重量为 nums[i],价值也为 nums[i]
                dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
            }
        }
        
        
        if (dp[target] == target)
        {
            return true;
        }
        return false;
    }
};