分割等和子集

题目

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

注意:

每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:

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

输出: true

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

示例 2:

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

输出: false

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

答案
class Solution {

    public boolean canPartition(int[] nums) {
        int len = nums.length;
        int sum=0,maxNum=0;

        if (len<2){//当数组元素小于2时不可分,返回false
            return false;
        }
        for (int num:nums){
            sum += num;
            maxNum = Math.max(maxNum, num);
        }
        if (sum%2!=0){//所有元素之后为奇数,不可分,返回false
            return false;
        }
        int target=sum/2;
        if (maxNum>target){//最大元素大于目标数,不可能分成两个和相等的数组
            return false;
        }

        //创造一个[len][target+1]的二维数组,其中len表示数组元素个数,target表示目标值。
        //数组从0开始,所以i-1为当前要分的个数,可通过nums[i]找到数组元素值,j表示当前要达到的目标值,在这i-1个元素的情况下,可以加出j则为true
        //需要得到len个元素是否能获得target所以为[len][target+1],最后[len-1][target]即为答案。
        //该二维数组是可以加出j即为true,而不是要等分为两份为j的数组。
        boolean[][] dp = new boolean[len][target+1];//boolean型数据默认为false。


        //当j为0时,为true,可以理解为可以把元素分为空集和其他所有集,空集那部分则可以加为0。
        for (int i=0;i<len;i++){
            dp[i][0] = true;
        }
        //当i=0,可用元素只有一个,则只有j=此元素才能加出j。
        dp[0][nums[0]] = true;

        //从i=1开始,依次增加元素
        for (int i=1;i<len;i++){
            for (int j=1;j<target+1;j++){

                //当最新的元素大于j时,加成j不需要这个元素,则是否能加成j与前几个元素情况相同。
                //如:假设i=2,有三个元素1,2,3,j=6,此时为true;
                //    当i+1为3后,第四个元素为7。7>6,那么是否能组成6与7无关,而与1,2,3有关,即dp[i][j]=dp[i-1][j]
                if (j<nums[i]) {
                    dp[i][j] = dp[i - 1][j];
                }

                //当最新元素小于j时,如果dp[i-1][j]为true,即之前的元素就能加出j,那么此时dp[i][j]为true。
                //如果dp[i-1][j]为false也有可能这个元素可以组成j,此情况可以转为某个数x+nums[i]=j,则x=j-nums[i]
                //那么可以转化为前几个元素能否组成x,dp[i][j]=dp[i-1][x]=dp[i-1][j-nums[i]]
                //上述两种情况有一个为true,则为true。
                else {
                    dp[i][j] = (dp[i-1][j-nums[i]] | dp[i-1][j]);
                }
            }
        }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值