46. 携带研究材料(01背包二维数组)

46. 携带研究材料(01背包二维数组)

题目是给定一个物品的重量数组weight,和物品对应的价值数组value。另外给了背包需要装多少种物品,和背包的容量(即输入两个数组 + 背包所考虑的物品种类category和背包的容量bagweight)

  • dp数组的定义,下标表示什么含义。

dp[i][j] 表示 容量为 j 的背包从编号 [0, i] 之间选取物品进行存放所能达到的最大价值。

其中,横轴上的坐标可以考虑为是背包的容量,纵轴的坐标可以考虑为是物品的种类。因此,横轴的长度是bagweight + 1,这样的话,最后一个位置就可以表示当背包容量为bagweight时所能容纳的 x 种物品种类的最大价值。纵轴的长度为物品的种类数目,其中纵轴坐标0表示第一种物品。

  • dp数组的递推公式

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

  1. dp[i][j] == dp[i-1][j] 的情况是表示背包当前不考虑将第i个物品放进来,为什么?因为空间不够!
  2. dp[i][j] == value[i] + dp[i-1][j - weight[i]] 的情况是表示当前背包容量充足,能够把第i个物品容纳进来,那就直接是加上了物品对应的价值value[i],而此时背包空间剩余 j - weight[i],那你要在这剩余的空间下最大化背包的价值,那就是以当前剩余的容量 j - weight[i],从前i-1个物品中进行选择,来得到最大价值即dp[i-1][j - weight[i]] 。 两者相加就是此时背包j在前i个物品下所能到达的最大价值。
  • dp数组的初始化

由于横轴的第一个坐标是表示容量为0的背包,因此第一列都为0.

由于纵轴的第一个坐标是表示第一个物品的情况,因此针对第一行容量可变的背包需要进行初始化。具体地,当背包容量 ≥ 物品重量时, 此时背包的价值就是物品的价值(因为只有一种物品)。(这一步for循环的遍历可以不用从0开始,可以从物品的重量开始进行遍历到bagweight+1,左闭右开)

对第一行和第一列初始化后,剩余的就可以基于初始化的dp数组和递推公式求得。

  • 遍历顺序

根据背包的大小和物品的种类,从左上到右下。第一行和第一列可以不进行遍历。

  • 打印dp数组

输出dp数组进行查看。另外,要明确dp数组的定义是什么,因此最后你要输出的是dp[category-1][bagweight],即背包容量为bagweight下对所有物品category进行选择下所能达到的最大价值。由于物品种类的编号都向左偏移了一位,因此横轴输入是categoty - 1。

Code

python 复制代码
### 将input的数据按空字符串进行切分,map是将input的内容转换为int类型
cur_categroy, cur_room = map(int, input().split())
### 将输入的数字字符串转换为数组
all_category_room = list(map(int, input().split()))
all_value = list(map(int, input().split()))

### 1. dp数组定义: dp[i][j]: 容量为j的背包的容纳[0,i]的物品时所具有的最大价值
### 横轴是物品的最大容量。纵轴上物品的种类
# dp = [[0] * (max(all_category_room)+1) for _ in range(len(all_category_room))]   

dp = [[0] * (cur_room+1) for _ in range(cur_categroy)]       
# print("起始的dp", dp)

# ### 2. 递推公式
# ### dp[i][j] = max(dp[i-1][j], dp[i-1][j-room[i]] + value[i]) 
# ### dp[i][j]: 容量为j的背包的容纳[0,i]的物品时所具有的价值 = max(不容i物品进来前的背包价值, 容纳i物品进来后当前背包的容量变化和价值变化)

# ### 3. dp数组初始化
for j in range(cur_room+1):
    if j >= all_category_room[0]:           ### 背包的容量 大于等于 物品0的容量
        dp[0][j] = all_value[0]    ### 此时背包因为含有物品0,因此价值为物品0的价值value[0]

# print("初始化后的dp", dp)

# ### 4. 遍历顺序
for i in range(1, cur_categroy):
    for j in range(1, 1+cur_room):
        if j >= all_category_room[i]:
            dp[i][j] = max(dp[i-1][j], dp[i-1][j-all_category_room[i]] + all_value[i])
        else:
            dp[i][j] = dp[i-1][j]

# ### 5. 打印dp数组
print(dp[cur_categroy-1][cur_room])        
####需要搞清楚dp数组的定义,才知道为什么这里是print dp[cur_categroy-1][cur_room], 表示在cur_room的背包空间下存放[0,cur_categroy-1]中物品的最大价值

Code

python 复制代码
cur_categoty, cur_weight = map(int, input().
split())
all_category = list(map(int, input().split()))
all_value = list(map(int, input().split()))

### 1. dp数组定义,dp[j]表示背包容量为j时的最大价值
dp = [0] * (cur_weight + 1)
### 2. dp数组遍历公式, 不要给我自动续写
### dp[j] = max( dp[j], dp[j-weight[i]]+value[i] )

### 3. dp数组初始化,不要给我自动续写
### 都初始化为0就行

### 4. dp数组遍历顺序,从后往前
for i in range(len(all_category)):    ### 遍历物品  
    for j in range (len(dp)-1, -1, -1):  ### dp数组遍历,从后往前
        # print("i,j", i,j)
        if j >= all_category[i]:
            dp[j] = max(dp[j], dp[j -  all_category[i]]+ all_value[i])

### 5. 打印dp数组
# print(dp)

print(dp[cur_weight]) 

Code

python 复制代码
#
# @lc app=leetcode.cn id=416 lang=python
#
# [416] 分割等和子集
#

# @lc code=start
class Solution(object):
    def canPartition(self, nums):
        """
        :type nums: List[int]
        :rtype: bool
        """

        if len(nums) == 1:
            return False
        if len(nums) == 2:
            if nums[0] == nums[1]:
                return True
            else:
                return False
        ### 关键:那么只要找到集合里能够出现 sum / 2 的子集总和,就算是可以分割成两个相同元素和子集了。
            
        ### 1. dp数组含义。
        ### dp[i][j] == 容量为j的背包,可以从[0,i]的元素下取出若干个,所实现的最大价值
        ### dp[j] == 容量为j的背包当前所能实现的最大价值
        sums = sum(nums)
        if sums % 2 == 1:
            return False
        else:
            target = sums / 2 
        dp = [0] * (sums + 1)   #### 错误的思路,把每个数字的重量都看成是1,但应该是正比关系,数字的价值 == 数字的重量

        ### 2. dp数组递推公式
        ### dp[j] = max(d[j], dp[j-1]+nums[i])

        ### 3. dp shuzu chushihua 
        ### dou chushihua wei 0

        ### 4. 遍历顺序,一维dp conghouwangqian
        for i in range (len(nums)):
            for j in range(len(dp)-1, -1, -1):
                if j >= nums[i]:
                    dp[j] = max(dp[j], dp[j-nums[i]]+nums[i])  
        
        ### 5. print dp
        print(dp)
        for m in range(len(dp)):
            if dp[m] == target:
                return True
        
        return False

# @lc code=end

### 给你一个 只包含正整数 的 非空 数组 nums 。
### 请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
### 输入:nums = [1,5,11,5]
### 输出:true
### 解释:数组可以分割成 [1, 5, 5] 和 [11] 。


if __name__ == "__main__":

    nums = [2, 2, 1, 1]
    # nums = [1,5,11,5]
    solution = Solution()
    result = solution.canPartition(nums)
    print(result)
相关推荐
原来是猿几秒前
算法中 cin/cout 超时?聊聊它与 printf/scanf 的性能差异
算法
老赵聊算法、大模型备案5 分钟前
“清朗·整治AI应用乱象”专项行动深度解读:从资质合规视角看AI应用新规
大数据·人工智能·算法·安全·aigc
Hello.Reader11 分钟前
算法基础(二)——算法为什么是一种核心技术
算法
rit843249911 分钟前
电容层析成像(ECT)的ART算法MATLAB演示实例
开发语言·算法·matlab
故事和你9113 分钟前
洛谷-算法2-4-字符串2
开发语言·数据结构·c++·算法·深度优先·动态规划·图论
cpp_250113 分钟前
P3374 【模板】树状数组 1
数据结构·c++·算法·题解·洛谷·树状数组
郝学胜-神的一滴13 分钟前
干货版《算法导论》 02 :算法效率核心解密
java·开发语言·数据结构·c++·python·算法
stolentime13 分钟前
AT_agc061_d [AGC061D] Almost Multiplication Table题解
c++·算法·构造
WL_Aurora14 分钟前
Python 算法基础篇之回溯
python·算法
智者知已应修善业15 分钟前
【51单片机控制的交通信号灯三按键切换调节时分秒加减】2023-8-26
c++·经验分享·笔记·算法·51单片机