LeetCode 416. Partition Equal Subset Sum

问题描述

这里写图片描述

问题分析

  • 给定一个非空正整数数组,能否将数组分成两部分(子集),使两部分和相等本质上就是在一个数组中取任意个数相加,能否加出 sum/2,是一个简化版的背包问题。
  • 首先,如果 sum为奇数,肯定找不出这样的两部分,直接返回false即可。
  • 当为偶数时,才采用类似处理背包问题的动态规划法,只不过该题更为简单,没有weight,少一个参数。具体见代码实现中的注释

代码实现

  • 递归(TLE)
    public boolean canPartition(int[] nums) {
        if (nums == null || nums.length == 0) {
            return false;
        }
        int sum = 0;
        for (int i = 0; i < nums.length; ++i) {
            sum += nums[i];
        }
        return (sum & 1) == 0 && findHalf(nums, nums.length - 1, sum/2);
    }

    //表示[0 ~ i]能否加出remain
    public boolean findHalf(int[] nums, int i, int remain) {
        if (remain == 0) {
            return true;
        }
        if (i < 0 || remain < 0) {
            return false;
        }
        return findHalf(nums, i - 1, remain) || findHalf(nums, i - 1, remain - nums[i]);
    }
  • 原始动态规划
    public boolean canPartition(int[] nums) {
        if (nums == null || nums.length == 0) {
            return false;
        }
        int sum = 0;
        for (int i = 0; i < nums.length; ++i) {
            sum += nums[i];
        }
        if ((sum & 1) != 0) {
            return false;
        }
        int col = sum / 2 + 1;
        //dp[i][j] 表示 nums[0 ~ i] 能否加出 j
        boolean[][] dp = new boolean[nums.length][col];
        //初始化第一行
        dp[0][0] = true;
        for (int j = 1; j < col; ++j ) {
            dp[0][j] = (nums[0] == j);
        }
        for (int i = 1; i < nums.length; ++i) {
            dp[i][0] = true;
            for (int j = 1; j < col; ++j) {
                dp[i][j] = dp[i - 1][j] || (j - nums[i] >= 0 && dp[i - 1][j - nums[i]]);
            }
        }
        return dp[nums.length - 1][col - 1];
    }
  • 动态规划优化空间
    public boolean canPartition(int[] nums) {
        if (nums == null || nums.length == 0) {
            return false;
        }
        int sum = 0;
        for (int i = 0; i < nums.length; ++i) {
            sum += nums[i];
        }
        if ((sum & 1) != 0) {
            return false;
        }
        int len = sum / 2 + 1;
        boolean[] dp = new boolean[len];
        ////初始化第一行
        dp[0] = true;
        for (int j = 1; j < len; ++j) {
            dp[j] = (nums[0] == j);
        }
        for (int i = 1; i < nums.length; ++i) {
            for (int j = len - 1; j >= nums[i]; --j) { //注意此处可以直接j >= nums[i]
                dp[j] = dp[j] || dp[j - nums[i]];
            }
        }
        return dp[len - 1];
    }

猜你喜欢

转载自blog.csdn.net/zjxxyz123/article/details/80200419