代码随想录算法训练营第45天|动态规划part04|01背包问题 二维、 01背包问题 一维、 416. 分割等和子集

代码随想录算法训练营第45天|动态规划part04|01背包问题 二维、 01背包问题 一维、 416. 分割等和子集

动态规划:01背包理论基础

动态规划:01背包理论基础

在这里插入图片描述

对于面试的话,其实掌握01背包,和完全背包,就够用了,最多可以再来一个多重背包。

所以背包问题的理论基础重中之重是01背包,一定要理解透!

所以我先通过纯01背包问题,把01背包原理讲清楚,后续再讲解leetcode题目的时候,重点就是讲解如何转化为01背包问题了。

动态规划:01背包理论基础(滚动数组)

动态规划:01背包理论基础(滚动数组)

416. 分割等和子集

416. 分割等和子集

代码随想录

思路:

一个商品如果可以重复多次放入是完全背包,而只能放入一次是01背包,写法还是不一样的。

回归主题:首先,本题要求集合里能否出现总和为 sum / 2 的子集。

那么来一一对应一下本题,看看背包问题如何来解决。

只有确定了如下四点,才能把01背包问题套到本题上来。

  • 背包的体积为sum / 2
  • 背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
  • 背包如果正好装满,说明找到了总和为 sum / 2 的子集。
  • 背包中每一个元素是不可重复放入。

动规五部曲分析如下:

  1. 确定dp数组以及下标的含义

01背包中,dp[j] 表示: 容量为j的背包,所背的物品价值最大可以为dp[j]。

本题中每一个元素的数值既是重量,也是价值。

套到本题,dp[j]表示 背包总容量(所能装的总重量)是j,放进物品后,背的最大重量为dp[j]。

那么如果背包容量为target, dp[target]就是装满 背包之后的重量,所以 当 dp[target] == target 的时候,背包就装满了。

有录友可能想,那还有装不满的时候?

拿输入数组 [1, 5, 11, 5],举例, dp[7] 只能等于 6,因为 只能放进 1 和 5。

而dp[6] 就可以等于6了,放进1 和 5,那么dp[6] == 6,说明背包装满了。

  1. 确定递推公式

01背包的递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

本题,相当于背包里放入数值,那么物品i的重量是nums[i],其价值也是nums[i]。

所以递推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);

  1. dp数组如何初始化
    在01背包,一维dp如何初始化,已经讲过,

从dp[j]的定义来看,首先dp[0]一定是0。

如果题目给的价值都是正整数那么非0下标都初始化为0就可以了,如果题目给的价值有负数,那么非0下标就要初始化为负无穷。

这样才能让dp数组在递推的过程中取得最大的价值,而不是被初始值覆盖了。

本题题目中 只包含正整数的非空数组,所以非0下标的元素初始化为0就可以了。

  1. 确定遍历顺序

在动态规划:关于01背包问题,你该了解这些!(滚动数组) (opens new window)中就已经说明:如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历!

  1. 举例推导dp数组

dp[j]的数值一定是小于等于j的。

**如果dp[j] == j 说明,集合中的子集总和正好可以凑成总和j,**理解这一点很重要。

代码:

python

class Solution(object):
    def canPartition(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        # 确定背包容量
        if sum(nums) % 2 == 1: return False
        target = sum(nums) // 2

        # 1. 确定dp数组
        dp = [0] * (target+1) # dp[j]表示容量为j的背包存放的最大价值为多少

        # 2. 递推公式
        # dp[j] = max(dp[j], dp[j-nums[i]] + nums[i]) # max中的dp[j]表示上一层中容量为j的背包加入物品0到i-1后背包的可以具备的最大价值,dp[j-nums[i]] + nums[i]表示背包容量为j减去当前物品i的体积后还能装下的最大值(也就是没有当前物品i时背包的最大价值)再加上有物品i的价值就是当前背包的最大价值

        # 3. dp数组的初始化
        dp[0] = 0 # 表示容量为0时,背包所能容下的最大价值为0
        # dp[1:]初始化为0,因为只包含正整数,取max时不受影响,可以初始化为0,但是如果有负数,就应该初始化为负无穷

        # 4. 确定遍历顺序
        for i in range(len(nums)): # 物品的数量
            for j in reversed(range(nums[i], target+1)):
                dp[j] = max(dp[j], dp[j-nums[i]] + nums[i])
        '''
        在遍历顺序上有几点疑惑:
        1. 为什么必须是先遍历物品,在遍历容量
        2. 为什么容量是从后向前
        3. 为什么容量要大于等于nums[i]也就是物品i的体积
        解释:
        1. 因为2是从后向前遍历,所以不能轻易调换位置
        2. 从前往后,
        二维dp:i表示[0-i]的物品里任意取,j表示背包容量,dp[i][j]表示最大价值
        一维dp:j表示背包容量,dp[j]表示容量为j的最大价值
        用一维代替二维实际上是用一维表示二维上一层[0:i-1]下各个容量的最大价值
        但如果一旦正序遍历了,那么物品0就会被重复加入多次!
        3. 为什么j要从大于等于nums[i](物品i的体积)开始,因为j<nums[i]时,dp[j]放不下物品i,也就是还是保持上一次的最大价值,也就是本层循环没必要进行任何操作;并j-nums[i]必须大于等于0才有意义
        '''

        if dp[-1] == target:
            return True
        else:
            return False
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值