首页 > 编程语言 >代码随想录算法训练营第四十四天|01 背包、动态规划:01背包理论基础(滚动数组)、416. 分割等和子集

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

时间:2024-05-30 19:29:07浏览次数:27  
标签:遍历 weight 随想录 value 01 物品 背包 dp

01 背包

文档讲解:代码随想录

题目链接:46. 携带研究材料(第六期模拟笔试)

有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

 暴力解法:每个物品都有放与不放两种状态,复杂度是2的n次方

动态规划:

dp[i][j] :下标为[0,i]之间的物品,任取放进容量为j的背包里的最大价值

必须是把dp数组定义为答案,然后找递推关系,反推到起点,然后双循环搞定

 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]   )

dp数组初始化

初始化很有讲究

由 dp[i][j]  = max(dp[i-1][j] ,dp[i-1][j-weight[i]] + value[i]   )可以看出:dp[i][j]由它的左上方和正上方推出来的

所以要初始化第一行和第一列,dp[i][j]与它原来的值没有关联,所以初始化成什么值都可以

 遍历顺序

两层for循环,一个遍历物品,一个遍历背包的容量

对于二维数组实现的01背包,两层for循环是可以颠倒的,先遍历物品还是先遍历背包都可以

当前元素是由正上方和左上方推导出来的,如果要求当前元素的值,一定要确定左上方有值,正上方有值

如果先遍历物品,再遍历背包,那么就是一行一行的遍历,满足上方和左上方有数据,所以这个遍历方式是可以的

如果先遍历背包,再遍历物品,那么就是一列一列的遍历,满足上方和左上方有数据,所以这个遍历方式也是可以的

所以,对于二维数组实现的01背包,无论先遍历背包还是物品,要求的格子的值,满足左上方和正上方都有值,所以两种遍历方式都可以

def backpack(M,N,space,value):
    bp = [[0 for j in range(N+1)] for i in range(M)]
    #第一列都是0,初始化第一行
    for i in range(1,N+1):
        if space[0] > i:
            continue
        else:
            bp[0][i] = value[0]
    
    #先遍历物品,再遍历背包
    for i in range(1,M):
        for j in range(1,N+1):
            if space[i] <= j: #能放下i
            
                bp[i][j] = max(bp[i-1][j],bp[i-1][j-space[i]]+value[i])
            else:#不能放下i

                bp[i][j] =bp[i-1][j]
    return (bp[M-1][N])
    
M, N = map(int, input().split())
space = list(map(int, input().split()))
value = list(map(int, input().split()))

# 计算并输出结果
print(backpack(M, N, space, value))

注意:dp[i][j]  = max(dp[i-1][j] ,dp[i-1][j-weight[i]] + value[i]  ) 要保证[j-weight[i]]下标大于等于0才可以,也就是容量大于等于weight[i],也就是说,只有当容量大于i所占的空间时,才会考虑放不放i的问题,反之,那么肯定不会放物品i

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

用一维数组实现01背包

用二维数组实现01背包:dp[i][j]  = max(dp[i-1][j] ,dp[i-1][j-weight[i]] + value[i] ) 

可以看出当前层的数值是由上一层推导出来的,是不是就可以把i-1的数据拷贝到第i层,直接在第i层上进行操作,不断覆盖原来的数据

dp[j]:容量为j的背包所能装的物品的最大价值是dp[j]

递推公式

不放物品i的状态就是dp[j]

放物品i:dp[j-weight[i]] + value[i]

dp[j] = max(dp[j] ,dp[j-weight[i]] + value[i] ) 

初始化

dp[0] = 0

应该初始化一个非0中的最小值,这样取最大值不会覆盖递归出来的数据

遍历顺序

放物品i:dp[j-weight] + value[i]   这样写是因为在二维数组中dp[i-1][j-weight]中一定是没有物品i的,并且给物品i额外保留了位置,

举一个例子:物品0的重量weight[0] = 1,价值value[0] = 15

如果正序遍历

dp[1] = dp[1 - weight[0]] + value[0] = 15  容量为1的背包中对于放不放物品0的判断

dp[2] = dp[2 - weight[0]] + value[0] = 30   容量为2的背包中对于放不放物品0的判断,但是dp[2 - weight[0]]之前已经判断过是否加入0背包了,可能是加入的,这就会造成重复

倒叙遍历保证每个物品被添加一次

不太懂

416. 分割等和子集

文档讲解:代码随想录

题目链接:. - 力扣(LeetCode)

这是一个0、1背包问题

背包问题,大家都知道,有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

背包问题有多种背包方式,常见的有:01背包、完全背包、多重背包、分组背包和混合背包等等。

要注意题目描述中商品是不是可以重复放入。

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

要明确本题中我们要使用的是01背包,因为元素我们只能用一次。

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

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

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

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

以上分析完,我们就可以套用01背包,来解决这个问题了。

重量和价值一样,当背包正好装满之后,最大价值就是11,那么就是true

递推公式

dp[j] = max(dp[j] ,dp[j-num[i]] + num[i] ) 

初始化

dp[0] = 0

遍历顺序

和上题一样,是倒叙遍历

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        #先判断是不是奇数
        if sum(nums) % 2!=0:
            return False
        sum_num = int(sum(nums)/2)
        print(sum_num)
        dp = [0] * (sum_num+1) 
        
        for i in nums:#遍历物品
            for j in range(sum_num,i-1,-1):
                dp[j] = max(dp[j],dp[j-i]+i) #j要大于等于i才有意义,但是不知道为什么j小于i时,没有报错
        if dp[-1] == sum_num:
            return True
        else:
            return False

疑问:j要大于等于i才有意义,但是不知道为什么j小于i时,没有报错。只是结果会有问题

标签:遍历,weight,随想录,value,01,物品,背包,dp
From: https://blog.csdn.net/qq_52149213/article/details/139305565

相关文章

  • 代码随想录算法训练营Day55 | 583. 两个字符串的删除操作、72. 编辑距离、编辑距离总
    本文目录583.两个字符串的删除操作做题看文章72.编辑距离做题看文章编辑距离总结篇以往忽略的知识点小结个人体会583.两个字符串的删除操作代码随想录:583.两个字符串的删除操作Leetcode:583.两个字符串的删除操作做题找出最长公共子序列,然后用两个字符串的......
  • CSP历年复赛题-P1075 [NOIP2012 普及组] 质因数分解
    原题链接:https://www.luogu.com.cn/problem/P1075题意解读:求n的两个素因子中较大的一个。解题思路:数论的简单题,关键在于要知道一定有一个素因子不超过sqrt(n),而另一个素因子必然大于或等于sqrt(n),这样才能减少枚举时间。100分代码:#include<bits/stdc++.h>usingnamespaces......
  • CSP历年复赛题-P1310 [NOIP2011 普及组] 表达式的值
    原题链接:https://www.luogu.com.cn/problem/P1310题意解读:+代表按位或运算,*代表按位与运算,给定一个没有填数字的表达式,要求结果为0的数字方案数。解题思路:下面一步一步,由浅入深的来解决本题思路一(20分做法):观察得知,20%的数据,只有10个符号,且没有括号,也就是对应数字最多11个,可以......
  • flutter - [01] Dart概述
    题记部分 一、什么是dartdart是由谷歌开发的计算机编程语言,可以被用于web、服务器、移动应用和物联网等领域的开发dart诞生于2011年,号称要取代JavaScript。但是过去的几年中一直不温不火,直到Flutter出现后,被人们重新重视。要学习Flutter,必须首先得学习Dart。dart官网:htt......
  • M语言--罗氏801流水线自动稀释功能改造
    1、需求描述根据科室要求,对于HCG等项目需要改造自动稀释功能,以解放人工挑选标本再上机,提高效率根据本院需求整理,自动稀释有两个需求点:A:临床开医嘱时备注有稀释字样的,按照规定倍数进行稀释B:生殖类标本,先根据预设的倍数稀释,如果效果不理想,则检验师再根据实际情况向LIS系......
  • Navicat远程连接阿里云mysql失败,提示2013,2003错误解决方案
    前情提要总结下使用过的各种解决方式,如修改cnf,修改安全组端口,修改防火墙,总有一款方案适合你(如果使用其他方式解决请评论补充,感谢)环境:本文全部使用yum方式安装服务,使用阿里云服务器centos7下文需要格外注意手动配置端口的部分确认已安装好mysql服务(yum安装)......
  • 【NOIP2017普及组复赛】题2:图书管理员
    题2:图书管理员【题目描述】图书馆中每本书都有一个图书编码,可以用于快速检索图书,这个图书编码是一个正整数。每位借书的读者手中有一个需求码,这个需求码也是一个正整数。如果一本书的图书编码恰好以读者的需求码结尾,那么这本书就是这位读者所需要的。小......
  • 洛谷 P8614 [蓝桥杯 2014 省 A] 波动数列 的题解
    题目大意求满足和为sss且ti=......
  • Debug-012-el-popover 使用 doClose() 关闭窗口不生效的处理方案
     前言:    今天上午碰见一个非常奇怪的情况:一样的方法实现的功能,效果却不一样。两个页面都是使用的doClose()去关闭的el-popover,其中有一个就是不生效,找不同找了半天,始终不得其解。请看效果吧:el-popover-doClose()-ok视频1(el-popover-doClose()-关闭成功的)e......
  • 联邦学习DLG攻击_NeurIPS2019_Deep Leakage from Gradients_深度梯度泄露,模型逆向攻击
    联邦学习联邦学习DLG攻击_NeurIPS2019_DeepLeakagefromGradients_深度梯度泄露发现了梯度可以倒推参数的问题文章目录要开始看些安全的内容了!一、Abstract二、Introduction2.1联邦学习的背景:2.2提出疑问:「梯度共用」计划有否保障每名参加者的训练资料集的私隐?2.......