Leetcode 416. Split Equal Sum Subsets (01 Knapsack)

  • Leetcode 416. Split Equal Sum Subsets (01 Knapsack)
  • topic
    • You are given a non-empty array nums containing only positive integers. Please judge whether this array can be divided into two subsets so that the sum of the elements of the two subsets is equal.
    • 1 <= nums.length <= 200
    • 1 <= nums[i] <= 100
  • solution
    • Dynamic programming (01 backpack): first consider the special case: since all elements are positive integers, a single element or all elements cannot be divided if the sum is an odd number.
    • Then there is the general situation: the topic can be transformed into selecting some elements and making the sum half of the sum of all elements, so you can think of 01 backpack
    • Define the state (sub-problem): dp[i][j] is the subscript (including) the first i elements, and whether j can appear, initialization: j is 0, it is true, otherwise it is false, and finally judge dp[n- 1][m/2]
    • Transfer equation: if adding the current element dp[i-1][j-num[i]] or not adding dp[i-1][j] is true, then dp[i][j] is true (dp[i][j] can be used i-1][j-num[i]] | dp[i-1][j])
    • Space compression: Since i is only related to i-1, you can use the scrolling array dp[2][m/2], and if you traverse j in reverse order, you can only use the previous elements when traversing, and the assignment will not affect the previous elements, so use the rolling array dp[m/2]
    • The number of elements is n, the sum of all elements is m, time complexity: O(n*m), space complexity: O(m)
  • the code
    /**
     * 动态规划(01 背包):首先思考特殊情况:由于所有元素均为正整数、因此 单个元素 或 所有元素和为奇数 则无法划分,
     * 然后是一般情况:题目可以转化为选择部分元素、使其和为所有元素和的一半,因此可以想到 01 背包
     * 定义状态(子问题):dp[i][j] 为下标(含)前 i 个元素、和为 j 是否可以出现,初始化:j 为 0 是 true、否则为 false,最后判断 dp[n-1][m/2]
     * 转移方程式:如果加当前元素 dp[i-1][j-num[i]] 或不加 dp[i-1][j] 为 true、则 dp[i][j] 为 true(可用 dp[i-1][j-num[i]] | dp[i-1][j])
     * 空间压缩:由于 i 仅与 i-1 相关,因此可以使用滚动数组 dp[2][m/2],同时如果遍历 j 时倒序、遍历时可以只用使用之前的元素、同时赋值不会影响之前的元素,因此使用滚动数组 dp[m/2]
     * 元素个数为 n,所有元素之和为 m,时间复杂度:O(n*m),空间复杂度:O(m)
     */
    private boolean solution(int[] nums) {
    
    
        // 判空 + 特殊情况(小于俩元素则 false)
        if (nums == null || nums.length <= 1) {
    
    
            return false;
        }

        // 获取所有元素之和 + 特殊情况(和为奇数则 false)
        int len = nums.length;
        int total = getTotal(nums, len);
        if ((total & 1) == 1) {
    
    
            return false;
        }
        int half = (total >> 1);

        // 定义状态 + 初始化
        boolean[] dp = new boolean[half + 1];
        dp[0] = true;

        // 通过转移方程式定义数据 + 空间压缩
        for (int i = 0; i < len; i++) {
    
    
            for (int j = half; j >= nums[i]; j--) {
    
    
                dp[j] = (dp[j] | dp[j - nums[i]]);
            }
        }

        return dp[half];
    }

    /**
     * 获取所有元素之和
     */
    private int getTotal(int[] nums, int len) {
    
    
        int total = 0;
        for (int i = 0; i < len; i++) {
    
    
            total += nums[i];
        }
        return total;
    }

Guess you like

Origin blog.csdn.net/qq_33530115/article/details/131119203