赞
踩
给你一个只包含正整数的非空数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例 1:
输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。
本题与 0-1 背包问题有一个很大的不同,即:
作为「0-1 背包问题」,它的特点是:「每个数只能用一次」。解决的基本思路是:物品一个一个选,容量也一点一点增加去考虑,这一点是「动态规划」的思想,特别重要。
具体做法是:画一个 nums.length 行,target + 1 列的表格。这里 nums.length 是物品的个数,target 是背包的容量。nums.length 行表示一个一个物品考虑,target + 1多出来的那 1 列,表示背包容量从 0 开始考虑。很多时候,我们需要考虑这个容量为 0 的数值。
状态定义:a[i][j]表示从数组的 [0, i] 这个子区间内挑选一些正整数,每个数只能用一次,使得这些数的和恰好等于 j。
状态转移方程:很多时候,状态转移方程思考的角度是「分类讨论」,对于「0-1 背包问题」而言就是「当前考虑到的数字选与不选」。
状态转移方程:
a[i][j] = a[i - 1][j] or a[i - 1][j - nums[i]]
j - nums[i] 作为数组的下标,一定得保证大于等于 0 ,因此 nums[i] <= j;
注意到一种非常特殊的情况:j 恰好等于 nums[i],即单独 nums[i] 这个数恰好等于此时「背包的容积」 j,这也是符合题意的。
因此完整的状态转移方程是:
dp[i−1][j] | 至少是这个答案,如果 dp[i−1][j] 为真,直接计算下一个状态 | |
dp[i][j]= | true | nums[i] = j |
dp[i−1][j−nums[i]]. | nums[i] < j |
- var canPartition = function(nums) {
- let sum = 0;
-
- for(let i = 0; i < nums.length; i++){
- sum = sum + nums[i];
- }
- // 数组中都是正整数 但总和为奇数时是无法分成两个相等正整数的 还有就是当数组中元素少于等于一个时是无法再分的 子集不可以是空数组也不可以是整个数组
- if(sum % 2 != 0 || nums.length <= 1){
- return false;
- }
- let target = sum/2;
- // 创建二维数组 初值为0
- const a = new Array(nums.length).fill(0).map(() => new Array(target+1).fill(0));
- // 定义边界 不能越界 一旦第一个数超过了target 说明指定返回false 因为这个数已经超过了整个数组和的一半 剩下的所有数加在一起也是比它小的
- if(nums[0] <= target){
- a[0][nums[0]] = true;
- }
-
- for(let i = 1; i < nums.length; i++){
- for(let j = 0; j <= target; j++){
- // 将上一行的结果复制下来,在下面更改需要的地方 nums[i]<j
- a[i][j] = a[i-1][j];
- //
- if(nums[i] == j){
- a[i][j] = true;
- // continue;
- };
- // nums[i] < j
- if(nums[i] < j){
- a[i][j] = a[i-1][j] || a[i-1][j-nums[i]];
- }
- }
- }
- // 返回表格最右下角的值
- return a[nums.length-1][target];
- };

「0-1 背包问题」常规优化:「状态数组」从二维降到一维,减少空间复杂度。
- public class Solution {
-
- public boolean canPartition(int[] nums) {
- int len = nums.length;
- int sum = 0;
- for (int num : nums) {
- sum += num;
- }
- if ((sum & 1) == 1) {
- return false;
- }
-
- int target = sum / 2;
- boolean[] dp = new boolean[target + 1];
- dp[0] = true;
-
- if (nums[0] <= target) {
- dp[nums[0]] = true;
- }
- for (int i = 1; i < len; 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];
- }
- }
-
-

- /**
- * @param {number[]} nums
- * @return {boolean}
- */
- var canPartition = function(nums) {
- let sum = 0;
- for(let i = 0; i < nums.length; i++){
- sum += nums[i];
- }
- if(sum % 2 != 0 || nums.length < 2){
- return false;
- }
-
- let target = sum/2;
- // 定义一维数组 初值为false
- const a = new Array(target+1).fill(0);
- // 将a[0]赋为true
- a[0] = true;
- if(nums[0] <= target){
- a[nums[0]] = true;
- }
-
- for(let i = 1; i < nums.length; i++){
- for(let j = target; j >= nums[i]; j--){
- if(a[target] == true){
- return true;
- }
- // a[j] = a[j] 是用来保留上一次循环已经确定下来为true的j 不再改变它
- a[j] = a[j] || a[j-nums[i]];
- }
- }
- return a[target];
- };

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。