Partition Equal Subset Sum

406 篇文章 0 订阅
406 篇文章 0 订阅

1,题目要求

Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.

Note:

  1. Each of the array element will not exceed 100.
  2. The array size will not exceed 200.

Example 1:
Input: [1, 5, 11, 5]
Output: true
Explanation: The array can be partitioned as [1, 5, 5] and [11].

Example 2:
Input: [1, 2, 3, 5]
Output: false
Explanation: The array cannot be partitioned into equal sum subsets.

给定仅包含正整数的非空数组,查找是否可以将数组划分为两个子集,使得两个子集中的元素总和相等。

2,题目思路

对于这道题,给定一个数组,判断该数组能否划分成两个子集,两个集合的和是相等的。

这道题,是之前的一道题的解法基础:
Target Sum
上面这道题,在底层用的就是子集部分和的概念,当时并没有对这个部分和的计算做详细讲解,而这道题就是对这个问题的详解。

首先,这道题的解法的理念基础是,对于一个数组nums,假设我们存在一个集合P和集合N,它们的和分别为sum(P)sum(PN
假设集合P与N是满足条件的,那么我们有:

  • sum (P) + sum(N) = sum(nums)
  • sum( P ) = sum(N)

因此,我们有以下结论:

  • 如果存在这样的子集,我们有sum(P) = sum(nums) / 2

即,问题就转化为了,在一个集合中是否存在一个子集,它的和是整个集合的和的一半。

在方法上,我们可以使用动态规划的办法。其中,我们设置DP[j]表示对于值jnums是否存在一个子集,其和等于j
基本思想在于:

DP[i] = DP[i] || DP[i - num];

然而动态规划的方法运算速度较慢,这里,我们可以使用bitset的方法实现动态规划的思想。

  • 对于bitset,它存储的是二进制数位
  • bitset就像一个bool类型的数组一样,但是有空间优化:bitset中的一个元素一半只占1bit,相当于一个char所占空间的八分之一。
  • bitset中的每一个元素都可以被单独访问,例如对于一个叫做foo的bitset,foo[3]访问了它的第四个元素,这点类似于数组。
  • bitset有一个特性:整数类型和布尔数组都可以转化为bitset
  • bitset的大小在编译时就需要确定。

具体用法参考博客:
C++ bitset——高端压位卡常题必备STL

对于这道题,我们用bitset来替代DPbits[j]的含义与DP[j]的含义相同。

我们使用左移操作或操作来记录所有的可能的加和。
对于例子,nums = [2,3,5],初始情况下bits设置为1,对nums进行遍历:

  • nums = 2,bits = 101,表示nums的子集和可以是0和2;
  • nums = 3,bits = 101101, 表示nums的子集和可以是0、2、3、5;
  • nums = 5,bits = 10110101101,表示nums的子集和可以是0、2、3、5、7、8、10。

于是,我们只需要看bits[(2+3+5)/2]是否为1即可。

另外,题目给出的是最大元素的值不超过100,元素个数不超过200,因此我们有sum的最大值为:

  • 100*200 = 20000

因此,sum/2我们有:10000,又因为我们需要一位记录0,因此,bitset的大小为10001。

两个利用位运算的简写:

  • 判断是否是偶数:sum & 1,如果为1,说明是奇数,如果是0,说明是偶数;
  • 除以二:sum >> 1

3,代码实现

1,动态规划

int x = []() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    return 0;
}();

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        if(nums.size() == 0)
            return true;
        int sum = accumulate(nums.begin(), nums.end(), 0);
        int target = sum/2;
        
        if(sum%2 != 0)
            return false;
        
        vector<bool> DP(target+1, false);
        DP[0] = true;
        for(auto n : nums)
            for(int i = target;i >= n;i--)
                DP[i] = DP[i] || DP[i - n];
        return DP[target];
        
    }
};

2,利用bitset实现动态规划思想

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        bitset<10001> bits(1);
        int sum = accumulate(nums.begin(), nums.end(), 0);
        for (auto n : nums) bits |= bits << n;
            return !(sum & 1) && bits[sum >> 1];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值