代码随想录动态规划——分割等和子集

这篇博客探讨了一个关于数组元素能否平均分成两部分的问题,通过01背包的动态规划思想来解决。核心思路是找到集合中和为目标值一半的子集。文章提供了详细的解题步骤,包括确定dp数组、递推公式、初始化dp数组和遍历顺序,并给出了Java代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

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

注意: 每个数组中的元素不会超过 100 数组的大小不会超过 200

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

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

提示:

1 <= nums.length <= 200 1 <= nums[i] <= 100

思路

核心思路: 只要找到集合里面能够出现 sum/2 的子集总和,就算是可以分割成两个相同的元素和子集了。

01背包问题:N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

其中一个商品可以重复多次放入就是完全背包,而只能放入一次就是01背包

对于本题需要考虑以下四点,才能套用01背包:

  • 背包的体积为sum / 2
  • 背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
  • 背包如果正好装满,说明找到了总和为 sum / 2 的子集
  • 背包中每一个元素是不可重复放入

动规五部曲:

  1. 确定dp数组和下标含义
    01背包中,dp[j] 表示: 容量为j的背包,所背的物品价值可以最大为dp[j]
    套到本题,dp[j]表示背包总容量是j,最大可以凑成j的子集总和为dp[j]

  2. 确定递推公式
    01背包的递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
    本题,相当于背包里放入数值,那么物品i的重量是nums[i],其价值也是nums[i]
    所以递推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);

  3. dp数组初始化
    dp[j]的定义来看,首先dp[0]一定是0,如果题目给的价值都是正整数那么非0下标都初始化为0就可以了,如果题目给的价值有负数,那么非0下标就要初始化为负无穷。
    这样才能让dp数组在递归公式的过程中取的最大的价值,而不是被初始值覆盖了,本题题目中 只包含正整数的非空数组,所以非0下标的元素初始化为0就可以了

  4. 确定遍历顺序
    因为使用的是一维dp数组,所以遍历物品的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历(个人巧记不喜勿Cue:把东西往包里放,即先物品,再背包,放是先放到最里面再往外放,所以倒着放)

  5. 举例推导dp数组
    如果dp[j] == j 表明,集合中的子集总数正好可以凑成总和j
    以[1,5,11,5]为例:
    在这里插入图片描述

java代码如下:

class Solution {
    public boolean canPartition(int[] nums) {
        if(nums == null || nums.length == 0) return false;
        int n = nums.length;
        int sum = 0;
        for(int num : nums){
            sum += num;
        }
        //总和为奇数,不能平分
        if(sum % 2 != 0) return false;
        int target = sum / 2;
        int[] dp = new int[target + 1];
        for(int i = 0; i < n; i++){//先遍历物品
            for(int j = target; j >= nums[i]; j--){//倒序遍历背包,物品 i 的重量是 nums[i],其价值也是 nums[i]
                dp[j] = Math.max(dp[j], dp[j-nums[i]] + nums[i]);
            }
        }
        return dp[target] == target;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HDU-五七小卡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值