代码随想录算法训练营第35天|01背包理论基础、01背包理论基础(滚动数组)、416. 分割等和子集

打卡Day35

1.01背包理论基础

题目链接:01背包理论基础

文档讲解: 代码随想录

01背包:

有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weighti,得到的价值是valuei 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。它的暴力解法,每个物品的状态只有两个,取或者不取,可以使用回溯法搜索出所有情况,时间复杂度为 o ( 2 n ) o(2^n) o(2n)。

动规五部曲:

(1)确定数组和下标含义

dp i j 表示从下标0~i的物品里任意取,放进容量为 j 的背包,价值总和最大值

(2)确定递推关系式

可以从两个角度进行分析,对于dp i j ,可以放入物品 i,也可以不放。不放物品 i,由dp i-1 j 。放入物品 i,由dp i-1 j - weight\[i] + value i 。因此递推公式为dp i j = max(dp i-1 j ,dp i-1 j - weight\[i] + value i )。

(3)初始化

从递推关系式出发,dp0 j 需要赋值,放入物品 0 时,各种背包容量的价值总和,其中当背包容量小于weight0时,不放入物品,价值为0,反之为value0。当背包容量为0时,放不进任何物品,则dp i 0 = 0。

(4)遍历顺序

观察递推关系式,dp i j 主要由dp i-1 j 和dp i-1 j - weight\[i]求到,只要上方和左上方有值就可以,因此两层循环遍历背包和物品的顺序无论先后,都可以得到答案。

(5)打印数组

python 复制代码
M, N = [int(x) for x in input().split()]
space = [int(x) for x in input().split()]
value = [int(x) for x in input().split()]

#任取0~i的物品放入背包容量j的最大价值,i行j列
dp = [[0] * (N + 1) for _ in range(M)] 

#初始化
for j in range(space[0], N + 1):
    dp[0][j] = value[0]

#遍历
for i in range(1, M):
    for j in range(1,N + 1):
        if j >= space[i]:
            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - space[i]] + value[i])
        else:
            dp[i][j] = dp[i - 1][j]

print(dp[M - 1][N])

2.01背包理论基础(滚动数组)

题目链接:01背包理论基础

文档讲解: 代码随想录

(1)确定数组和下标的含义

dp j 表示背包容量为 j 时物品的最大价值

(2)递推关系式

二维数组的递推关系完全可以将dpi - 1那一层拷贝到dpi上,因此,递推关系式为dpj = max(dpj, dpj - weight\[i] + valuei)

(3)初始化

一定要和dp数组的定义吻合,dp0 = 0,因为递推关系式是取最大值,因此其余位置可以初始化为0

(4)遍历顺序

两层循环,一个 i,一个 j。但 j 是从后往前,因为如果从前往后,物品0会被重复加入多次。二维数组dpij都是通过上一层即dpi - 1j计算而来,本层的dpij并不会被覆盖!二维数组循环遍历的时候,背包和物品的顺序可以对调,但是在一维数组中,不可以换,因为一旦先遍历背包,背包是倒序遍历的,这样子每个dpj就只会放入一个物品。

(5)打印数组

python 复制代码
kind, bagspace = [int(x) for x in input().split()]
space = [int(x) for x in input().split()]
value = [int(x) for x in input().split()]

#新建dp数组,初始化为0
dp = [0] * (bagspace + 1)

for i in range(kind):
    for j in range(bagspace, space[i] - 1, -1):
            dp[j] = max(dp[j], dp[j - space[i]] + value[i])

print(dp[bagspace])

3.416. 分割等和子集

题目链接:416. 分割等和子集

文档讲解: 代码随想录

如果可以分割为两个子集数值和相等,那么就可以抽象为背包问题,背包容量为该数组值总和的一半,问是否存在元素将其装满。这是一个01背包问题,因为其中每个数字只能用一次。同时,需要注意的是,每个元素的重量和价值均为元素的数值。

(1)确定dp数组和下标

dp j 表示容量 j 的背包可以装下的最大重量

(2)递推关系式

dpj = max(dpj, dpj - nums\[i] + numsi)

(3)初始化

从定义来看,dp0 = 0,因为是取最大值,为了在取值时不被初始值覆盖,因此选择非负的最小值0,来初始化其余下标不为0的元素

(4)遍历顺序

采用一维数组,因此 i 从前往后遍历数组元素, j 从后往前遍历背包容量

(5)打印数组

python 复制代码
class Solution(object):
    def canPartition(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        kind = len(nums)
        summ = sum(nums)
        if summ % 2 == 1:
            return False
        else:
            bagweight = summ / 2
        
        dp = [0] * (bagweight + 1)
        for i in range(kind):
            for j in range(bagweight, nums[i] - 1, -1):
                dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
        
        if dp[bagweight] == bagweight:
            return True
        else:
            return False
python 复制代码
class Solution(object):
    def canPartition(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """
        #精简版
        if sum(nums) % 2 != 0:
            return False
        target = sum(nums) / 2
        dp = [0] * (target + 1)
        for num in nums:
            for j in range(target, num - 1, -1):
                dp[j] = max(dp[j], dp[j - num] + num)
        if dp[target] ==target:
            return True
        return False
相关推荐
kkeeper~7 小时前
0基础C语言积跬步之数据在内存中的存储
c语言·数据结构·算法
wabs6668 小时前
关于贪心算法的一些自我总结【力扣45.跳跃游戏II】【灵感来源:代码随想录】
算法·贪心算法·复盘
2401_876964139 小时前
【湖北专升本】2026湖北专升本真题PDF+备考资料汇总
数据结构·人工智能·经验分享·深度学习·算法·计算机视觉
嗝o゚9 小时前
CANN GE 算子融合——融合算法与调度策略
算法·昇腾·cann·ge
小江的记录本9 小时前
【JVM虚拟机】垃圾回收GC:垃圾回收算法:标记-清除、标记-复制、标记-整理、分代收集(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·算法·安全·面试
Ulyanov11 小时前
用声明式语法重新定义Python桌面UI:QML+PySide6现代开发入门(一)
开发语言·python·算法·ui·系统仿真·雷达电子对抗仿真
数据科学小丫11 小时前
特征工程处理
人工智能·算法·机器学习
z落落11 小时前
C#参数区别
java·算法·c#
c2385612 小时前
vector(下)
数据结构·算法
z落落12 小时前
C# 冒泡排序+选择排序 + Array.Sort 自定义排序
数据结构·算法