LeetCode刷题系列 -- 416. 分割等和子集

题目:

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

注意:

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

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

输出: true

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

示例 2:

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

输出: false

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

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

思路:

    

「0 - 1」 背包问题的思路
作为「0-1 背包问题」,它的特点是:「每个数只能用一次」。解决的基本思路是:物品一个一个选,容量也一点一点增加去考虑,这一点是「动态规划」的思想,特别重要。
在实际生活中,我们也是这样做的,一个一个地尝试把候选物品放入「背包」,通过比较得出一个物品要不要拿走。

具体做法是:画一个 len 行,target + 1 列的表格。这里 len 是物品的个数,target 是背包的容量。len 行表示一个一个物品考虑,target + 1多出来的那 1 列,表示背包容量从 0 开始考虑。很多时候,我们需要考虑这个容量为 0 的数值。

状态与状态转移方程
状态定义:dp[i][j]表示从数组的 [0, i] 这个子区间内挑选一些正整数,每个数只能用一次,使得这些数的和恰好等于 j。
状态转移方程:很多时候,状态转移方程思考的角度是「分类讨论」,对于「0-1 背包问题」而言就是「当前考虑到的数字选与不选」。
不选择 nums[i],如果在 [0, i - 1] 这个子区间内已经有一部分元素,使得它们的和为 j ,那么 dp[i][j] = true;
选择 nums[i],如果在 [0, i - 1] 这个子区间内就得找到一部分元素,使得它们的和为 j - nums[i]。
状态转移方程:


dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i]]
一般写出状态转移方程以后,就需要考虑初始化条件。

j - nums[i] 作为数组的下标,一定得保证大于等于 0 ,因此 nums[i] <= j;
注意到一种非常特殊的情况:j 恰好等于 nums[i],即单独 nums[j] 这个数恰好等于此时「背包的容积」 j,这也是符合题意的。
因此完整的状态转移方程是:

\text{dp}[i][j]= \begin{cases} \text{dp}[i - 1][j], & 至少是这个答案,如果 \ \text{dp}[i - 1][j] \ 为真,直接计算下一个状态 \\ \text{true}, & \text{nums[i] = j} \\ \text{dp}[i - 1][j - nums[i]]. & \text{nums[i] < j} \end{cases}
dp[i][j]= 







​    
  
dp[i−1][j],
true,
dp[i−1][j−nums[i]].
​    
  
至少是这个答案,如果 dp[i−1][j] 为真,直接计算下一个状态
nums[i] = j
nums[i] < j
​    
 

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/0-1-bei-bao-wen-ti-xiang-jie-zhen-dui-ben-ti-de-yo/
来源:力扣(LeetCode)
 

Java代码:

class Solution {
    public  boolean  canPartition(int[]  nums){
        int  len    = nums.length;
        int  sum = Arrays.stream(nums).sum();
        if(sum%2 != 0){
            return false;
        }
        int target = sum / 2;
        boolean[][] dp = new boolean[len][target+1];

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

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

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值