这是我参与8月更文挑战的第27天,活动详情查看:8月更文挑战
这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。
168. 分割等和子集 (partition-equal-subset-sum)
标签
- 中等
题目
给你一个 只包含正整数 的 非空 数组 nums
。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例 1
输入: nums = [1,5,11,5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2
输入: nums = [1,2,3,5]
输出: false
解释: 数组不能分割成两个元素和相等的子集。
基本思路
看上去是可以分割成小问题的问题都能用动态规划或者递归解决。
从动态规划这篇我们了解到动态规划的基本步骤是下面三步:
- 寻找最优子结构(状态表示)
- 归纳状态转移方程(状态计算)
- 边界初始化
我们还是根据之前的基本步骤来
- 状态表示:
要使和为整个数组和的一半, 我们想 就定一个 halfSum = sum / 2
那下面还是用dp[i][j]
来看, 其实我们可以设 从数组的[0, i]
这个子区间内选取若干元素,令和等于 j
那么 dp[n][halfSum]
就是我们要求的最终答案
- 状态转移方程:
接下来就是遍历每一个数, 这又有点像背包问题, 遇到每个数有两个选择, 放入背包和不放入。
i
从 index 的[0...n] 遍历
- 不选取
nums[i]
:dp[i][j] = dp[i-1][j]
- 选取
nums[i]
:dp[i][j] = dp[i-1][j - nums[i]]
- 边界初始化:
- 上面选择有个边界前提
j - nums[i] >= 0
- halfSum 必须是整数
- 如果不选取任何正整数,则被选取的正整数等于 0。因此对于
i
在[0...n]
都有dp[i][0] = true
。
写法实现
var canPartition = function(nums) {
let len = nums.length
if (len < 2) return false;
let sum = nums.reduce((cur, acc) => cur + acc, 0)
// 判断奇偶性
if (sum % 2 !== 0) return false
// 取一半
let halfSum = sum / 2
// 建立 dp[i][j]
let dp = new Array(len).fill(0).map(it => new Array(halfSum + 1).fill(false))
// 边界, 从 i ~ n 一个不选的话,和为 0 肯定满足
for (let i = 0; i < len; i++) {
dp[i][0] = true;
}
for (let i = 1; i < len; i++) {
for (let j = 1; j <= halfSum; j++) {
if (j >= nums[i]) {
// || 或者,只要有一个成立,说明整能找到一个,能找到一个就成
dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
} else {
// 如果 j < nums[i] 当前元素比和都大,那还加入啥,只能期待等于前面的结果有找到的
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[len - 1][halfSum]
};
nums = [1, 5, 11, 5]
console.log(canPartition(nums))
注释什么的我觉得已经写清楚了。
今天是8月更文的最后两天,看上去动态规划有很多种类了,我们稍微对这种类型的问题总结下。
首先我们如何想到这个题目用动态规划来做呢?
我的思路就是,当我其他数据结构或算法我一下子反应不过来,或者感觉不太好做,条件要求考虑太多的时候,又遇到问题中有 求最XX
, 怎样XX最好
,还有没啥简单思路的字符串问题
,我就会考虑到用动态规划的时间又到了。
分析动态规划问题,要从最后的结果入手,反推出你的状态定义,如何设置,能让你能轻易表示出最后的答案结果呢,比如这题就是怎么定义让你能表示出 dp[len-1][halfSum]
就是结果
然后就根据条件来进行小问题求解往大问题上转化,如何进行状态转移,其实就是找递推公式。
最后思考边界条件,一半都是 0 行 0 列
有问题
最后,画图是解决这类问题的好方式,画图辅助思考事半功倍,而且优化拍平降维操作画图更直观。
另外向大家着重推荐下这个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列
今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友
Or 搜索我的微信号infinity_9368
,可以聊天说地
加我暗号 "天王盖地虎" 下一句的英文
,验证消息请发给我
presious tower shock the rever monster
,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧