【动态规划】力扣416:分割等和子集

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。

本题等价于 0-1 背包问题,设所有数字和为 sum,目标是选取一部分物品,使得它们的总和为 sum/2。这道题不需要考虑价值,因此只需要通过一个布尔值矩阵来表示状态转移矩阵。
注意边界条件的处理:

  • 根据数组的长度 n 判断数组是否可以被划分。如果 n<2,则不可能将数组分割成元素和相等的两个子集,因此直接返回 false。
  • 计算整个数组的元素和 sum 。如果 sum 是奇数,则不可能将数组分割成元素和相等的两个子集,因此直接返回 false。如果 sum 是偶数,则令 target= sum/2,需要判断是否可以从数组中选出一些数字,使得这些数字的和等于 target。
  • 一种取巧:找出最大元素 maxNum,如果 maxNum > target,则除了 maxNum 以外的所有元素之和一定小于 target,因此不可能将数组分割成元素和相等的两个子集,直接返回 false。
class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        total = sum(nums)
        n = len(nums)
        if total % 2 != 0 or n < 2:
            return False
        target = total // 2
        # dp = [True] + [False] * target
        dp = [False] * (target + 1) 
        dp[0] = True
        '''
        for i, num in enumerate(nums):
            for j in range(target, num - 1, -1):
                dp[j] |= dp[j - num]
        '''
        for i in range(n):
            for j in range(target, nums[i - 1] - 1, -1):
                dp[j] = dp[j] | dp[j - nums[i - 1]]
        return dp[target]

时间复杂度:O(n×target),其中 n 是数组的长度,target 是整个数组的元素和的一半。需要计算出所有的状态,每个状态在进行转移时的时间复杂度为 O(1)。
空间复杂度:O(target),其中 target 是整个数组的元素和的一半。空间复杂度取决于 dp 数组,进行空间优化的情况下空间复杂度可以降到 O(target)。

posted @   Vonos  阅读(96)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示