leetcode(力扣) 416. 分割等和子集 (动态规划 & 01背包问题)

文章目录

  • 题目描述
  • 思路分析
  • 完整代码

题目描述

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

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

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

思路分析

在做这道题之前一定要先看 01背包理论基础

这种题最难的不是写代码,也不是思路,而是怎么建模,要不是最近在练动态规划,我都不一定能想到用01背包去做这个题。

题目和01背包的转换:

题目给出一个数组nums,要求分成两个子集,使他们相等,这里要注意,只能分成两个子集。 所以问题转化成,求一个子集 使其等于 sum(nums) // 2 == target。如果有这样的子集 则返回True 否则返回False

背包容量就是 target, dp[j] 则代表 背包容量为j时能凑的最大的子集和为dp[j],最后如果 dp[target] == target 则就是找到了某个子集使其等于目标值。

这道题里的物品重量=物品价值=数组值=nums[i]。

动规五步走;
1.确定dp下标含义:
dp[j] 表示背包容量为 j时,最大的子集和为dp[j],
2.确定递推公式:
在问题转换哪里已经分析过了,直接拿传统背包问题来转换一下 就行。
递推公式:dp[j] = max(dp[j],dp[j-nums[i]]+nums[i])
3.初始化dp
这里的dp[j] 表示背包容量为j的最大子集和为dp[j]。
所以也就意味这容量能多大,我们初始化就得初始化多大的一维数组。
这里要看题目给的提示。

提示:
1 <= nums.length <= 200
1 <= nums[i] <= 100

所以最大应该是20000,而我们背包只需要一半的容量,直接初始化10000个[0]的一维数组就行了。

4.遍历顺序:
这个和传统的背包问题没什么区别,先遍历物品,在倒叙遍历背包。
外面循环范围是从0到len(nums)。
里面循环范围是从 target到nums[i](含),这里我一开始就写错了,注意这里的里层循环遍历的是背包容量,如果你的背包容量已经无法装下当前的 i 物品了,那就不用往后进行了,所以是到nums[i]截至。

5.模拟dp数组数值:
通常偷懒这一步,如果出错了可以回来看看dp的情况,方便debug。

完整代码

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
            if sum(nums) % 2 == 1:
                return False
            target = sum(nums) // 2
            dp = [0] * 10000
            # dp[i] 表示背包容量i 里的最大子集是dp[i]
            # 先物品,再背包,这里就想那个二维的数组格子就行了。
            for i in range(len(nums)):
                for j in range(target,nums[i]-1,-1):  # j表示背包容量,你的j必须大于目前遍历i的重量,才有往后的意义。
                    dp[j] = max(dp[j],dp[j-nums[i]]+nums[i])
            return dp[target] == target

你可能感兴趣的:(个人笔记,交流学习,leetcode,python)