leetcode416. 分割等和子集 (0-1背包)

传送门

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

1.每个数组中的元素不会超过 100
2. 数组的大小不会超过 200

示例: 输入: [1, 5, 11, 5] 输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].

关于0-1背包图解表格: 这里

0-1背包基础上解答本题: 这里

相当于找到一组数字,使其和为数组和的2分之1

方法1. 二维动态规划

public boolean canPartition(int[] nums) {
     
    int sum = 0;
    for (int num : nums) sum += num;
    if (sum % 2 != 0) return false;
    sum /= 2;
    boolean[][] dp = new boolean[nums.length][sum + 1];
    // 第一行初始化 让i从1开始遍历 因为 方程里包含有dp[i-1][j]
    if (nums[0] <= sum)  dp[0][nums[0]] = true;
    for (int i = 1; i < nums.length; ++i) {
     
        for (int j = 0; j <= sum; ++j) {
     
            if (nums[i] == j) dp[i][j] = true;
            else if (nums[i] < j) {
     
                dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];//两个都是i-1!!!
            }
        }
    }
    return dp[nums.length - 1][sum];
}

方法2. 一维动态规划(列优化)

上面每次扫描一行之后,更新下一行;那么只用一个列大小的dp数组,每扫描一行,把下一行的dp[j]拉下来更新这一行。

这题要注意的是: 列的更新要从后往前定义

因为列的值代表要组成的和,更新它的时候: dp[j] = dp[j] || dp[j - nums[i]]; 要用到j-nums[i]的dp值,显然在表格中,它的dp值在j的dp值的左边(因为j-nums[i]肯定比j小,列从左到右增大);

但是注意的是,这个j-nums[i]的dp值是上一层的,如果dp[j]从左到右更新,在更新dp[j]时,dp[j-nums[i]]的值已经更新完了,那么它取到的dp[j-nums[i]]的值就是这一层的值,不是上一层的啦。

   public boolean canPartition(int[] nums) {
     
        int sum = 0;
        for (int num : nums) sum += num;
        if (sum % 2 != 0) return false;
        sum /= 2;
        boolean[] dp = new boolean[sum + 1];
        // 第一行初始化 让i从1开始遍历 因为 方程里包含有dp[j]
        if (nums[0] <= sum)  dp[nums[0]] = true;
        for (int i = 1; i < nums.length; ++i) {
     
            for (int j = sum; j >= 0; --j) {
       // 列遍历方向变了
                if (nums[i] == j) dp[j] = true;
                else if (nums[i] < j) {
     
                    dp[j] = dp[j] || dp[j - nums[i]]; 
                }
            }
        }
        return dp[sum];
    }

你可能感兴趣的:(leetcode,动态规划,leetcode,背包)