416. 分割等和子集

题目

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

示例1

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

输出: true

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

示例2

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

输出: false

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

思路

思路一

  • 问题是数组中是否可以有两个子集,使得子集元素和相等。
  • 即找到一个子集,使得元素和等于数组所有元素和的一半。所以当数组所有元素的和为奇数时,必定不可能找到满足要求的子集。
  • 找到一个子集,使得元素和等于总和的一半,用half表示。即使用数组中的元素,是否能够填满大小为half的背包。
  • 使用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示,前j个数是否能填满大小为j的背包。 i = 0 i = 0 i=0可以假象为大小为0的背包, j = 0 j=0 j=0可以填满为0的背包,所以 d p [ 0 ] [ 0 ] dp[0][0] dp[0][0]就等于true。所以当j不等于0时,前j个数都可以填满0的背包。即 d p [ 0 ] [ j ] = t r u e dp[0][j]=true dp[0][j]=true
  • 对于 d p [ i ] [ j ] dp[i][j] dp[i][j],即前j个数是否能填满大小为i的背包,有两种情况。
    • 前j-1个数已经填满了大小为i的背包。即 d p [ i ] [ j − 1 ] = t r u e dp[i][j-1] = true dp[i][j1]=true
    • 第j个数设为num,背包容量i大于num,即第j个数占了num的容量,剩下i-num的容量,看前j-1个数是否能填满i-num的容量。即 d p [ i − n u m ] [ j − 1 ] dp[i-num][j-1] dp[inum][j1]

代码一

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        if ( nums.size() < 2 ) return false;

        int size = nums.size();
        int sum = 0;

        for ( auto& n : nums )
            sum += n;
        
        if ( ( sum & 1 ) == 1 ) return false;

        int half = ( sum >> 1 );

        /*二维数组*/
        int dp[half+1][size+1];
        memset( dp, 0, sizeof(dp) );

        dp[0][0] = 1;

        for ( int j = 1; j <= size; ++j ) {
            for ( int i = 0; i <= half; ++i ) {
                dp[i][j] = dp[i][j-1];

                if ( i >= nums[j-1] && !dp[i][j] )
                    dp[i][j] = dp[i-nums[j-1]][j-1];
            }
        }

        return dp[half][size];
    }
};

思路二

  • 从思路一看出,先关注第一个数是否能填满各个大小的背包,前两个数能否填满…前size个数是否能填满。假如现在考虑前j个数是否能填满i,我们考虑的是前j-1个数是否能填满,或者前j-1个是否能填满i-num,每次更新都用到了前面的值,画图的话就是左上的数据,所以可以用一维的数组存储。
  • dp[i]就表示i容量能否被填满。更新的时候从末尾向前更新,这样每次更新不会影响后面的更新。

代码二

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        if ( nums.size() < 2 ) return false;

        int size = nums.size();
        int sum = 0;

        for ( auto& n : nums )
            sum += n;
        
        if ( ( sum & 1 ) == 1 ) return false;

        int half = ( sum >> 1 );
        
        int dp[half+1];
        memset( dp, 0, sizeof(dp) );
        dp[0] = 1;

        for ( int j = 1; j <= size; ++j ) {
            for ( int i = half; i >= 1; --i ) {
                if ( i >= nums[j-1] )
                    dp[i] = dp[i] || dp[i-nums[j-1]];
            }
        }

        return dp[half];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值