分割等和子集

122 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情

一、题目

LeetCode 分割等和子集

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

示例 1:

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

示例 2:

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

提示:

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

二、题解

简单来说就是是否可以将数组中的某些元素提取出来组成的元素之后恰好等于其余剩下的元素之和,即两个子序列的元素之和等于原数组元素之和的一半。

方法一

根据题意可知,数组元素需大于一个才能拆分成两个子序列,同时两个子序列的元素之和要相等的话,那么原数组的元素之和就得是偶数,这样才能拆分成两个同等大小的数,同时如果原数组中最大元素值大于总和的一半时,也是无法拆分的,因为最大元素都大于总和的一半了,那么剩余的所以元素之和必定小于元素之和的一半,这样是无法拆分两个同等大小的子序列的。对于以上情况,都是不符合条件的可以校验下,直接发回false。除此之外可以使用动态规划来解决这个问题,首先定义一个二维数组dp,行大小与原数组大小相同,列大小就为目标子序列的大小(原数组总和的一半,下标从0开始的为了方便就加1)加一。那么dp[i][j]就表示数组nums[0]...nums[i]中提取部分元素之和是否能够等于j的。初始的当j为0时,不提取元素即可满足,所以dp[i][0] = true。遍历数组nums的元素num,同时也循环计算dp数组的j,当j >= num时,如果提取当前元素那么dp[i][j] = dp[i - 1][j - num];如果不提取当前元素那么dp[i][j] = dp[i - 1][j],是否提取当前元素都可能可以。当j < num时,无法选取当前元素那么dp[i][j] = dp[i - 1][j],最后返回计算的最终结果。

三、代码

方法一 Java代码

class Solution {
    public boolean canPartition(int[] nums) {
        int len = nums.length;
        if (len == 1) {
            return false;
        }
        int sum = 0;
        int maxNum = 0;
        for (int num : nums) {
            sum += num;
            maxNum = Math.max(maxNum, num);
        }
        if (sum % 2 != 0) {
            return false;
        }
        int target = sum / 2;
        if (maxNum > target) {
            return false;
        }
        boolean[][] dp = new boolean[len][target + 1];
        dp[0][nums[0]] = true;
        for (int i = 1; i < len; i++) {
            int num = nums[i];
            for (int j = 1; j <= target; j++) {
                if (j >= num) {
                    dp[i][j] = dp[i - 1][j] | dp[i - 1][j - num];
                } else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        return dp[len - 1][target];
    }
}

时间复杂度:O(n^2),需要遍历数组元素,计算每个子数组状态。

空间复杂度:O(n^2),需要一个二维数组来记录状态。