【动态规划】Leetcode 416. 分割等和子集【中等】

分割等和子集

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

示例 1:

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

解题思路

这是一个典型的动态规划问题,可以使用动态规划来解决

  • 1、计算数组 nums 的总和 sum。
  • 2、如果 sum 为奇数,那么无法将数组分割成两个和相等的子集,直接返回 false。
  • 3、将问题转化为背包问题:尝试从数组中挑选一些数字,使得它们的和等于 sum 的一半。 如果能够找到这样的数字组合,就说明可以将数组分割成两个和相等的子集。
  • 4、定义一个二维数组 dp,其中 dp[i][j] 表示在前 i 个数字中是否存在和为 j 的子集。
  • 5、初始化 dp 数组,当不选取任何数字时,可以得到和为 0 的子集,因此 dp[i][0] = true(0 <= i <= nums.length);当没有数字可选时,除了和为 0 的子集,其他和均不可能存在,因此 dp[0][j] = false(1 <= j <= sum/2)。
  • 6、遍历数组 nums,在每个位置考虑选取或不选取当前数字,更新 dp 数组。
  • 7、返回 dp[nums.length][sum/2],表示是否存在和为 sum/2 的子集。

Java实现

public class PartitionEqualSubsetSum {

    public boolean canPartition(int[] nums) {
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        if (sum % 2 != 0) {
            return false;
        }
        int target = sum / 2;
        boolean[][] dp = new boolean[nums.length + 1][target + 1];
        for (int i = 0; i <= nums.length; i++) {
            dp[i][0] = true;
        }
        /**
         * 这段代码是典型的动态规划解法,用于解决一个背包问题。在这里,dp[i][j]
         * 表示使用数组 nums 的前 i 个数字能否组成和为 j 的情况。
         *
         * 具体来说,内层的两个循环分别是:
         * 外层循环遍历数组 nums 的每个数字(从 1 到 nums.length)。
         * 内层循环遍历目标和 target 的可能取值(从 1 到 target)。
         *
         * 在每一次迭代中,我们需要考虑两种情况:
         * 1、如果当前数字 nums[i - 1] 大于当前目标和 j,则无法使用当前数字来达到目标和 j,
         * 因此 dp[i][j] 应该等于 dp[i - 1][j],即不使用当前数字 nums[i - 1];
         *
         * 2、如果当前数字 nums[i - 1] 小于等于当前目标和 j,则存在两种选择:
         * 不使用当前数字 nums[i - 1],即 dp[i][j] = dp[i - 1][j];
         * 使用当前数字 nums[i - 1],即 dp[i][j] = dp[i - 1][j - nums[i - 1]]。
         *  如果选择使用当前数字,则需要查看前一个状态 dp[i - 1][j - nums[i - 1]] 是否为 true,
         *  即在不考虑当前数字时,前一个状态能否达到目标和为 j - nums[i - 1] 的情况。
         *  如果为 true,则说明使用当前数字后,目标和为 j 的情况可以通过选择当前数字来实现。
         *
         * 因此,最终 dp[i][j] 的值是以上两种情况的逻辑或(||)结果。这样,
         * 在外层循环和内层循环的遍历结束后,dp[nums.length][target]
         * 就表示使用数组 nums 中的所有数字能否组成目标和为 target 的情况。
         */
        for (int i = 1; i <= nums.length; i++) {
            for (int j = 1; j <= target; j++) {
                if (nums[i - 1] > j) {
                    dp[i][j] = dp[i - 1][j];
                } else {
                    dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];
                }
            }
        }
        return dp[nums.length][target];
    }

    public static void main(String[] args) {
        PartitionEqualSubsetSum partitionEqualSubsetSum = new PartitionEqualSubsetSum();

        // Test case 1
        int[] nums1 = {1, 5, 11, 5};
        System.out.println(partitionEqualSubsetSum.canPartition(nums1)); // Output: true

        // Test case 2
        int[] nums2 = {1, 2, 3, 5};
        System.out.println(partitionEqualSubsetSum.canPartition(nums2)); // Output: false
    }
}

时间空间复杂度

  • 时间复杂度:遍历了一次数组nums,并使用了一个二维数组dp,时间复杂度为O(n * sum),其中n为数组nums的长度,sum为数组nums的总和。

  • 空间复杂度:使用了一个二维数组dp,空间复杂度为O(n * sum)。

相关推荐

  1. 动态规划Leetcode 416. 分割子集中等

    2024-04-27 06:30:03       13 阅读
  2. 动态规划 Leetcode 416 分割子集

    2024-04-27 06:30:03       20 阅读
  3. leetcode:416.分割子集

    2024-04-27 06:30:03       13 阅读
  4. LeetCode 416. 分割子集

    2024-04-27 06:30:03       13 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-27 06:30:03       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-27 06:30:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-27 06:30:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-27 06:30:03       18 阅读

热门阅读

  1. Jsoup爬虫

    2024-04-27 06:30:03       13 阅读
  2. 快速使用之Log4j2日志框架

    2024-04-27 06:30:03       11 阅读
  3. Nginx(三): 项目实战之conf

    2024-04-27 06:30:03       12 阅读
  4. DNA序列k-mers哈希映射和相似序列查找

    2024-04-27 06:30:03       13 阅读
  5. webpack和vite

    2024-04-27 06:30:03       12 阅读
  6. vue使用外部的模板

    2024-04-27 06:30:03       14 阅读
  7. 债市的牛熊

    2024-04-27 06:30:03       12 阅读