作者: 负雪明烛
id: fuxuemingzhu
个人博客: http://fuxuemingzhu.cn/


题目地址:https://leetcode.com/problems/partition-equal-subset-sum/description/

题目描述

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.

题目大意

判断是否可以把一组数字分成两堆,两堆数字的和相等。

解题方法

DFS

首先要判断所有数字的和是不是偶数,然后我们使用一个长度为2的数组进行保存我们要平分得到的target,这么做是我们可以通过使用-,+两种操作来跳过一些数字。同样是dfs,这里的dfs操作允许跳过某些位置去向下寻找,只要找到一个满足条件的就可以停止。而subsets的题不可以这么做,因为我们要找到所有的可能的答案。因此可以看做是两套模板。

Python代码:

class Solution:
def canPartition(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
_sum = sum(nums)
div, mod = divmod(_sum, 2)
if mod or max(nums) > div: return False
nums.sort(reverse = True)
target = [div] * 2
return self.dfs(nums, 0, target) def dfs(self, nums, index, target):
for i in range(2):
if target[i] >= nums[index]:
target[i] -= nums[index]
if target[i] == 0 or self.dfs(nums, index + 1, target): return True
target[i] += nums[index]
return False

动态规划

这个题其实是个0-1背包问题。所以可以使用动态规划求解。

求和是必须的,目标target等于和的一半。如果和不是偶数的话则一定不可能由数组构成出来,直接返回false.

首先定义dp数组为dp[i][j],其意义是使用前i个数字的和能不能构成整数j。我们需要把每个位置都进行遍历,同时也要对0~target之间的所有正整数进行遍历。很显然,状态转移的方程是,遍历到i位置时能不能构成target = 前面的数字的和能够成target || 前面的数字能构成target - nums[i]。这两个状态分别是选不选取nums[i]的两种情况,如果有一种情况成立即可。

状态转移方程如下:

dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]]

这个题的技巧和难点就在于,需要把数组的每个数字的和当做dp的一个状态,这个是很少见的,其实题目给的有提示:数组的每个数都是整数,并且数字不会超过200,数组长度不超过100,这些说明了数字的和不会太大不会太多。

具体C++代码如下:

class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = accumulate(nums.begin(), nums.end(), 0);
const int N = nums.size();
int target = sum >> 1;
if (sum % 2 != 0) return false;
//dp[i][j] means whether we can sum to j using first i numbers.
vector<vector<bool>> dp(N + 1, vector<bool>(sum + 1, false));
// every number can build number 0.
for (int i = 0; i <= N; ++i) {
dp[i][0] = true;
}
// but for position 0, it can build number nothing.
for (int j = 1; j <= target; ++j) {
dp[0][j] = false;
}
// anyway, position 0 can build number 0.
dp[0][0] = true;
for (int i = 1; i <= N; ++i) {
for (int j = 0; j <= target; ++j) {
if (j >= nums[i - 1])
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];
else
dp[i][j] = dp[i - 1][j];
}
}
return dp[N][target];
}
};

从上面的代码中可以看出,每个位置的状态只与其前面位置的状态有关,所以可以做状态压缩节约空间复杂度。

只使用一维dp数组,dp[j]表示从数组中任意取数字的和能不能构成j。状态转移方程就是忽略掉二维数组的第一个维度即可,即:

dp[j] = dp[j] || dp[j - nums[i]]

还要说一下,为什么需要从后向前更新dp,这是因为每个位置依赖与前面的一个位置加上nums[i],如果我们从前向后更新的话,那么dp[i - 2]会影响dp[i - 1],然后dp[i - 1]接着影响dp[i],即同样的一个nums[i]被反复使用了多次,结果肯定是不正确的。但是从后向前更新就没有问题了。

那么结合上面的分析,可以写出如下代码:

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

日期

2018 年 4 月 2 日 —— 要开始准备ACM了
2019 年 1 月 8 日 —— 别熬夜,我都开始有黑眼圈了。。

【LeetCode】416. Partition Equal Subset Sum 解题报告(Python & C++)的更多相关文章

  1. LN : leetcode 416 Partition Equal Subset Sum

    lc 416 Partition Equal Subset Sum 416 Partition Equal Subset Sum Given a non-empty array containing ...

  2. [LeetCode] 416. Partition Equal Subset Sum 相同子集和分割

    Given a non-empty array containing only positive integers, find if the array can be partitioned into ...

  3. Leetcode 416. Partition Equal Subset Sum

    Given a non-empty array containing only positive integers, find if the array can be partitioned into ...

  4. [leetcode]416. Partition Equal Subset Sum分割数组的和相同子集

    Given a non-empty array containing only positive integers, find if the array can be partitioned into ...

  5. 【leetcode】416. Partition Equal Subset Sum

    题目如下: 解题思路:对于这种判断是否的题目,首先看看动态规划能不能解决.本题可以看成是从nums中任选i个元素,判断其和是否为sum(nums)/2,很显然从nums中任选i个元素的和的取值范围是[ ...

  6. 416. Partition Equal Subset Sum

    题目: Given a non-empty array containing only positive integers, find if the array can be partitioned ...

  7. 416 Partition Equal Subset Sum 分割相同子集和

    详见:https://leetcode.com/problems/partition-equal-subset-sum/description/ C++: class Solution { publi ...

  8. LC 416. Partition Equal Subset Sum

    题目 Given a non-empty array containing only positive integers, find if the array can be partitioned i ...

  9. [刷题] 416 Partition Equal Subset Sum

    要求 非空数组的所有数字都是正整数,是否可以将这个数组的元素分成两部分,使得每部分的数字和相等 最多200个数字,每个数字最大为100 示例 [1,5,11,5],返回 true [1,2,3,5], ...

随机推荐

  1. git放弃修改,强制覆盖本地代码

    1.git fetch --all  //从远程拉取最新的代码 不merge 2.git reset --hard origin/develop  //使用指定分支的代码(此处develop)强制覆盖 ...

  2. git 新建分支并切换到该分支_Git 从master拉取代码创建新分支 并且再将修改合并到master...

    开发过程中会从master主分支copy到另一个开发分支: 1.切换到master分支 git  checkout  master 2.获取最新的代码 git pull origin master 3 ...

  3. MapReduce08 数据清洗(ETL)和压缩

    目录 数据清洗(ETL) ETL清洗案例 需求 需求分析 实现代码 编写WebLogMapper类 编写WebLogDriver类 打包到集群运行 压缩 概念 MR支持的压缩编码 压缩算法对比 压缩性 ...

  4. 日常Java 2021/11/2

    Java提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据.有关对象的类型的信息和存储在对象中数据的类型.将序列化对象写入文件之后,可以从文件中读取出来 ...

  5. 日常Java 2021/10/27

    java HashMap HashMap是一个散列表,它存储的内客是键值对(key-value)映射.HashMap实现了Map.接口,根据键的HashCode值存储数据,具有很快的访问速度,最多允许 ...

  6. 日常Java 2021/9/28

    字符串反转 package m; public class m { public static void main(String[] args) { //定义一个字符串 String str = &q ...

  7. Angular中@Output()的使用方法

    子component中的html文件 <button (click)="Send()">送出</button><br> 子component中的 ...

  8. hashtable深度探索

    1.什么是哈希表(hashtable)?为什么要发明哈希表? 首先回答第二个问题,在之前的数据结构中我们学习了数组,链表,二叉树等数据结构,记录在结构中的相对位置是随机的,和记录的关键字之前不存在确定 ...

  9. Android 极光推送集成

    集成Jpush 1.用Android Studio创建一个Demo 2.创建激光推送开发者账号,要创建极光推送开发者帐号,请访问极光推送官方网站https://www.jiguang.cn/push ...

  10. 银联acp手机支付总结

    总结: 1.手机调用后台服务端接口,获取银联返回的流水号tn 银联支付是请求后台,后台向银联下单,返回交易流水号,然后返回给用户,用户通过这个交易流水号,向银联发送请求,获取订单信息,然后再填写银行卡 ...