Leetcode416. 分割等和子集

170 阅读2分钟

要求:

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

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

示例:

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

输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.

思路:

  1. 先求和,和为奇数,说明肯定不能一分为二,直接返回false;

  2. target=sum/2;转化为0-1背包问题,容量为target,找到一种可以存放的方案即可,有一半可以存储,另一半一定也可以存储。

  3. 压缩状态的话,还不是很熟练,所以就按正常套路走吧

  4. 步骤:

    1. 确定base case:容量为0时,无论选择什么元素都是true;第一行中,某一列的容量刚好等于第一行的值时,为true
    2. 确定状态:状态变量有两个,一个是背包容量,一个是可选元素。dp[i][j]代表当前i个元素,背包的容量为j,有dp[i][j]种方法。
    3. 确定选择:背包容量够或是不够。不够的话dp[i][j]=dp[i-1][j];够的话又分为两种情况,选或者不选。选的话dp[i][j]=dp[i-1][j-nums[i]],不选的话dp[i][j]=dp[i-1][j]。
    4. 确定dp数组的定义:len代表传入的数组长度,target=sum/2。创建数组dp[len][target+1]。行数和元素数量一样,下标也对应起来,dp[i]=nums[i],列数要比target多一列,是因为考虑到target要包含0的情况,当然了,这一列都初始化为true,默认数组初始化的值都是false;

    代码:

class Solution {
    public boolean canPartition(int[] nums) {
        int len = nums.length;
        int sum = 0;
        for(int num: nums) sum += num;
        if (sum % 2 == 1) return false;
        int target = sum/2;
        boolean[][] dp = new boolean[len][target];
        
        for (int i = 0; i < len; i++) {
            dp[i][0] = true;
        }
        if (nums[0] <= target) dp[0][nums[0]] = true;
        
        for (int i = 0; i < len; i++) {
            for (int j = 0; j < target + 1; j++) {
                if (j-nums[i] < 0) {
                    dp[i][j] = dp[i-1][j];
                } else {
                    dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]];
                }
            }
        }
        return dp[len-1][target];//此处是按照索引来的,实际顺序其实是len,target+1
    }
}