动态规划:力扣416. 分割等和子集(巧妙转化为01背包问题)

本题是01背包的变体
1、题目描述:
在这里插入图片描述
2、题解:
动态规划:
1、状态定义;
2、状态转移方程;
3、初始化;
4、输出;
5、思考状态压缩。
可以用递归去求,但是会存在重叠子问题,加个备忘录可以解决重复问题。
需要先判断数组元素之和target是否为偶数,如果为奇数则不能分割满足题目要求的两个数组;否则令target为target自身的一半。(这是和背包问题的区别)
转化为01背包问题,其中数组的长度n=len(nums)为物品数,target为背包的容量。我们用打表格法去做题。
状态定义:dp[i][j]表示从数组的 [0, i] 这个子区间内挑选一些正整数,每个数只能用一次,使得这些数的和恰好等于 j。dp[i][j] = x 表示,对于前 i 个物品,当前背包的容量为 j 时,若 x 为 true,则说明可以恰好将背包装满,若 x 为 false,则说明不能恰好将背包装满。

状态转移方程:很多时候,状态转移方程思考的角度是“分类讨论”,对于“0-1 背包问题”而言就是“当前考虑到的数字选与不选”dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i]]。
解释:如果不把 nums[i] 算入子集,或者说你不把这第 i 个物品装入背包,那么是否能够恰好装满背包,取决于上一个状态 dp[i-1][j],继承之前的结果。
如果把 nums[i] 算入子集,或者说你把这第 i 个物品装入了背包,那么是否能够恰好装满背包,取决于状态 dp[i - 1][j-nums[i-1]]。

base case 就是 dp[…][0] = true 和 dp[0][…] = false,因为背包没有空间的时候,就相当于装满了,而当没有物品可选择的时候,肯定没办法装满背包
伪代码如下

#初始化 ,n个物品,背包的总容量为target
dp[n+1][target+1] 全为False
dp[:][0] = True
for i in [1...n]:
    for j in [1...target]:
        dp[i][j] = dp[i-1][j] or dp[i-1][j-nums[i-1]]
return dp[-1][-1]

处理一些边界问题,python代码如下:

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        #动态规划,01背包
        n = len(nums)
        target = sum(nums)
        if target % 2 != 0:
            return False
        target //= 2
        dp = [[False] * (target + 1) for _ in range(n+1)]
        for i in range(n+1):
                dp[i][0] = True
        for i in range(1,n+1):
            for j in range(1,target+1):
                if j >= nums[i-1]:
                    dp[i][j] = dp[i-1][j] or dp[i-1][j-nums[i-1]]
                else:
                    dp[i][j] = dp[i-1][j]
        return dp[-1][-1]

状态压缩:
上面的代码时间和空间复杂度都是O(N*len(target)),我们可以对空间做优化
伪代码:

#初始化 ,n个物品,背包的总容量为target
dp[target+1] 全为False
dp[0] = True
for i in [1...n]:
    for j in [target...1]:
        dp[j] = dp[j] or dp[j-nums[i-1]]
return dp[-1]

做一下边界处理,python代码如下:

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        #动态规划,01背包
        n = len(nums)
        target = sum(nums)
        if target % 2 != 0:
            return False
        target //= 2
        dp = [False] * (target + 1)
        dp[0] = True
        for i in range(1,n+1):
            for j in range(target,0,-1):
                if j >= nums[i-1]:
                    dp[j] = dpj] or dp[j-nums[i-1]]
        return dp[-1]

此时状态复杂度为O(target)
另外可以参考之前的博客动态规划入门:01背包问题,提供模板和思路,简单易懂好上手(同时可在相应网站上做练习)添加链接描述

也可以同步做一下LeetCode 474、494

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值