【分割等和子集】

16 篇文章 0 订阅
4 篇文章 0 订阅
本文介绍了如何使用动态规划方法解决LeetCode上的分割等和子集问题。通过将问题转化为0-1背包问题,首先判断数组长度和元素和的奇偶性,然后初始化动态规划状态表,并通过状态转移方程找到解决方案。最后,提供了C++代码实现。
摘要由CSDN通过智能技术生成

分割等和子集

此题来源于力扣(分割等和子集
题目要求:给你一个 只包含正整数的非空数组nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

示例1:
输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5][11]
示例 2:
输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。

解题思路:(动态规划)
一. 转换为 「0 - 1」 背包问题
  这道题可以换一种表述:给定一个只包含正整数的非空数组 n u m s [ 0 ] nums[0] nums[0],判断是否可以从数组中选出一些数字,使得这些数字的和等于整个数组的元素和的一半。因此这个问题可以转换成「0 - 1背包问题」。这道题与传统的「0 − 1 背包问题」的区别在于,传统的「0 − 1 背包问题」要求选取的物品的重量之和不能超过背包的总容量,这道题则要求选取的数字的和恰好等于整个数组的元素和的一半。类似于传统的「0 − 1 背包问题」,可以使用动态规划求解。即:
1:0-1 背包问题选取的物品的容积总量 不能超过 规定的总量。
2:本题选取的数字之和需要 恰好等于 规定的和的一半。
二. 在使用动态规划求解之前,首先需要进行以下判断
1:根据数组的长度 n n n 判断数组是否可以被划分。如果 n < 2 n<2 n<2,则不可能将数组分割成元素和相等的两个子集,因此直接返回 f a l s e false false
2:计算整个数组的元素和 s u m sum sum 以及最大元素 m a x N u m maxNum maxNum
  :如果 s u m sum sum 是奇数,则不可能将数组分割成元素和相等的两个子集,因此直接返回 f a l s e false false
  :如果 s u m sum sum 是偶数,则令 t a r g e t = s u m 2 target=\frac{sum}{2} target=2sum ,需要判断是否可以从数组中选出一些数字,使得这些数字的和等于 t a r g e t target target。如果 m a x N u m > t a r g e t maxNum>target maxNum>target,则除了 m a x N u m maxNum maxNum 以外的所有元素之和一定小于 t a r g e t target target,因此不可能将数组分割成元素和相等的两个子集,直接返回 f a l s e false false
三. 初始化状态表
  创建二维数组 d p dp dp,包含 n n n t a r g e t + 1 target+1 target+1 列,其中 d p [ i ] [ j ] dp[i][j] dp[i][j]表示从数组的 [ 0 , i ] [0,i] [0,i] 下标范围内选取若干个正整数(可以是0个),是否存在一种选取方案使得被选取的正整数的和等于 j j j。初始时, d p dp dp 中的全部元素都是 f a l s e false false
四. 边界情况的考虑
1:如果不选取任何正整数,则被选取的正整数等于0。因此对于所有 0 ≤ i < n 0≤i<n 0i<n,都有 d p [ i ] [ 0 ] = t r u e dp[i][0]=true dp[i][0]=true
2:当 i = = 0 i==0 i==0 时,只有一个正整数 n u m s [ 0 ] nums[0] nums[0] 可以被选取,因此 d p [ 0 ] [ n u m s [ 0 ] ] = t r u e dp[0][nums[0]]=true dp[0][nums[0]]=true
五. 状态的转移
  对于 i > 0 i>0 i>0 j > 0 j>0 j>0 的情况,如何确定 d p [ i ] [ j ] dp[i][j] dp[i][j] 的值?需要分别考虑以下两种情况。
1:如果 j ≥ n u m s [ i ] j≥nums[i] jnums[i],则对于当前的数字 n u m s [ i ] nums[i] nums[i],可以选取也可以不选取,两种情况只要有一个为 t r u e true true,就有 d p [ i ] [ j ] = t r u e dp[i][j]=true dp[i][j]=true
  :如果不选取 [ i ] n u m s [ i ] [i]nums[i] [i]nums[i],则 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j]=dp[i−1][j] dp[i][j]=dp[i1][j]
  :如果选取 [ i ] n u m s [ i ] [i]nums[i] [i]nums[i],则 d p [ i ] [ j ] = d p [ i − 1 ] [ j − n u m s [ i ] ] dp[i][j]=dp[i−1][j−nums[i]] dp[i][j]=dp[i1][jnums[i]]
2:如果 j < n u m s [ i ] j<nums[i] j<nums[i],则在选取的数字的和等于 j j j 的情况下无法选取当前的数字 n u m s [ i ] nums[i] nums[i],因此有 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j]=dp[i−1][j] dp[i][j]=dp[i1][j]
状态转移方程如下:
d p [ i ] [ j ] = { d p [ i − 1 ] [ j ]   ∣   d p [ i − 1 ] [ j − n u m s [ i ] ] , j ≥ n u m s [ i ] d p [ i − 1 ] [ j ] , j < n u m s [ i ] dp[i][j]= \left \{\begin{array}{cc} dp[i−1][j] \ |\ dp[i−1][j−nums[i]], &j≥nums[i]\\ dp[i−1][j], & j<nums[i] \end{array}\right. dp[i][j]={dp[i1][j]  dp[i1][jnums[i]],dp[i1][j],jnums[i]j<nums[i]
最终得到 d p [ n − 1 ] [ t a r g e t ] dp[n−1][target] dp[n1][target] 即为答案。
代码(c++)

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n = nums.size();
        if (n < 2) {
            return false;
        }
        int sum = accumulate(nums.begin(), nums.end(), 0);
        int maxNum = *max_element(nums.begin(), nums.end());
        if (sum & 1) {
            return false;
        }
        int target = sum / 2;
        if (maxNum > target) {
            return false;
        }
        vector<vector<int>> dp(n, vector<int>(target + 1, 0));
        for (int i = 0; i < n; i++) {
            dp[i][0] = true;
        }
        dp[0][nums[0]] = true;
        for (int i = 1; i < n; i++) {
            int num = nums[i];
            for (int j = 1; j <= target; j++) {
                if (j >= num) {
                    dp[i][j] = dp[i - 1][j] | dp[i - 1][j - num];
                } else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        return dp[n - 1][target];
    }
};

参考:https://leetcode-cn.com/problems/partition-equal-subset-sum/solution/fen-ge-deng-he-zi-ji-by-leetcode-solution/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值