zoukankan      html  css  js  c++  java
  • LeetCode416. 分割等和子集

    ☆☆☆☆☆思路:转化为0-1背包问题。 即,是否可以从输入数组中挑出一些正整数,使得这些数的和 等于 整个数组和的一半。

      本题与传统0-1背包问题的不同在于,传统0-1 背包问题要求 选取的物品的重量之和 不能超过 背包的容量;而本题选取的数字之和需要 恰好等于 规定的和的一半。

      这一点的区别,决定了初始化时,所有值应为False。因此,本题状态转移方程为 F(i, c) = F(i - 1, c) || F(i - 1, c - w(i) ), 其中F(n , c)表示 考虑将n个物品填满容量为C的背包。时间复杂度为 O(n * sum/2) = O(n * sum)

      Step1.状态定义:dp[i][j] 表示从数组的[0,i]下标范围内,选取若干正整数,是否存在一种选取方案使得被选取的正整数的和等于j, 初始时均为false。

      Step2.考虑边界情况:

        1. 如果不选取任何正整数,则被选取的正整数为0.因此,dp[i][0] = true

        2.当i==0时,只有一个正整数nums[0]可以选取,因此,dp[0][nums(0)] = true。

      Step3.状态转移:

      

    0-1背包,即数组中的元素不可重复使用。技巧为 nums放在外循环,target在内循环,且内循环倒序。

    class Solution {
        public boolean canPartition(int[] nums) {
            int n = nums.length;
            if (n < 2) return false;
            int sum = 0;
            for (int num : nums) {
                sum += num;
            }
            // 如果和是奇数,则不能被平分
            if ((sum & 1) == 1) {
                return false;
            }
            /*
            int target = sum / 2;
            // dp[i][j] 表示从数组的[0,i]下标范围内,选取若干正整数,
            //          是否存在一种选取方案使得被选取的正整数的和等于j
            boolean[][] dp = new boolean[n][target + 1]; // 背包容量 0 ~ target
            for (int i = 0; i < n; i++) {
                dp[i][0] = true; // 不选取任何正整数,则被选取的正整数和为0
            }
            if (nums[0] <= target) {
                dp[0][nums[0]] = true;
            }
            for (int i = 1; i < n; i++) {
                for (int j = 1; j <= target; j++) {
                    dp[i][j] = dp[i-1][j];
                    if (j >= nums[i]) {
                        dp[i][j] |= dp[i-1][j-nums[i]];
                    }
                }
            }
            return dp[n-1][target];
            */
            /**
             *  空间优化
             */
            int target = sum / 2;
            boolean[] dp = new boolean[target + 1];
            dp[0] = true;
            // 只考虑第一个num,看是否能填满对应的背包。
            if (nums[0] <= target) {
                dp[nums[0]] = true;
            }
    //        for (int i = 0; i <= target; i++) {
    //            dp[i] = (nums[0] == i);
    //        }
            for (int i = 1; i < n; i++) {
                for (int j = target; j >= nums[i]; j--) { // 注意要倒序计算
                    dp[j] = dp[j] || (dp[j-nums[i]]);
                }
            }
            return dp[target];
        }
    }
  • 相关阅读:
    【NOIP2017】奶酪
    【NOIP2017】时间复杂度
    【NOIP2005】过河
    【洛谷习题】垃圾陷阱
    dfs序
    bzoj2441 小W的问题
    彩色迷宫
    蛋糕与蛋挞
    树上倍增
    因数个数定理
  • 原文地址:https://www.cnblogs.com/HuangYJ/p/14217129.html
Copyright © 2011-2022 走看看