筷子与饺子
Published on 2019-11-11 04:08 in 暂未分类 with 筷子与饺子

416. 分割等和子集

416 分割等和子集

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

注意:

每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:

输入: [1, 5, 11, 5]

输出: true

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

示例 2:

输入: [1, 2, 3, 5]

输出: false

解释: 数组不能分割成两个元素和相等的子集.

思路


判断是否有等和子集,对数组求和,那么问题转化为,在数组是否有一个序列的和等于数组和的一半
问题变成了一个01背包问题;
利用动态规划来求解,普通解法是建立一个二维数组

\(dp[i][j]\) 表示从\(0\)\(i\)之间是否存在一种方案使得和为\(j\) 初始是 0即false;

  • 解决边界问题

\(dp[i][0] = true\) : 因为当和为\(0\)的时候,数组下标\(0\)\(i\)之间存在一种方案使得 和为\(0\) (什么元素都不选)
\(dp[0][nums[0]] = true\) : 因为当和为\(nums[0]\)的时候,选择\(nums[0]\)为方案

  • 解决递推问题

\(if(j>=nums[i])\) 那么\(nums[i]\) 可以不被选取,也可以被选取。只需要将两者情况取或(只要有一个为true 则dp[i][j]为true):
\(dp[i][j] = dp[i-1][j] | dp[i][j-nums[i]];\)

\(if(j<nums[i])\) 那么该元素不被选取:
\(dp[i][j] = dp[i-1][j];\)

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        if(n < 2) return false;
        int sum = accumulate(nums.begin(),nums.end(),0);
        int MaxNum = *max_element(nums.begin(),nums.end());
        //判断sum是否为奇数,按位与 最后一位是0 则为偶数,为1 则为奇数
        if(sum & 1) return false;
        int target = sum/2;  //转化为 0 1 背包问题,即判断数组中是否存在和为target 的序列

        if(MaxNum > target) return false;

        //定义动态规划数组 dp[i][j] 表示从0到i之间 有没有和为j的序列
        vector<vector<int> > dp(n,vector<int> (target+1,0));

        for(int i=0;i<n;i++)
        {
            dp[i][0] = true;
        }

        dp[0][nums[0]] = true;

        for(int i =1;i<n;i++)
        {
            int num = nums[i];
            for(int j=1;j<=target ;j++)
            {
                if(j>=num)
                {
                    dp[i][j] = dp[i-1][j] | dp[i-1][j-num];
                }
                else
                {
                    dp[i][j] = dp[i-1][j];
                }
            }

             if(dp[i][target]) return true;
        }

        return dp[n-1][target];
    }
};

优化空间


把dp二维数组优化为一维的,这是因为每一次状态转移,dp[i][j]的值只与上一行数组有关。

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        if(n < 2) return false;
        int sum = accumulate(nums.begin(),nums.end(),0);
        int MaxNum = *max_element(nums.begin(),nums.end());
        //判断sum是否为奇数,按位与 最后一位是0 则为偶数,为1 则为奇数
        if(sum & 1) return false;
        int target = sum/2;  //转化为 0 1 背包问题,即判断数组中是否存在和为target 的序列

        if(MaxNum > target) return false;

        vector<int> dp(target+1,0);
        dp[0] = true;
        if(nums[0] <= target)
            dp[nums[0]] = true;

        for(int i=1;i<n;i++)
        {
            for(int j = target;nums[i] <= j; j--)
            {
                if(dp[target])
                {
                    return true;
                }
                dp[j] = dp[j] | dp[j - nums[i]];
            }
        }
        return dp[target];
    }
};

链接

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum

posted @ 2020-10-11 12:50  筷子与饺子  阅读(124)  评论(0编辑  收藏  举报