首页 > 其他分享 >浅析背包问题

浅析背包问题

时间:2024-05-31 11:31:46浏览次数:21  
标签:背包 temp annex 问题 key 物品 浅析 dp

理解递推式(或动态规划转移方程)是解决动态规划问题的关键。如果你对这类问题不太理解,下面我将通过一个简化的例子和逐步解释来帮助你理解如何构建和使用递推式。

例子:0/1 背包问题

问题描述

给定一个容量为 W 的背包和 n 个物品,每个物品有一个重量 w[i] 和一个价值 v[i]。求如何选择物品放入背包,使得总价值最大。

动态规划基本思想
  1. 状态定义

    • dp[i][j] 表示前 i 个物品中选择若干放入容量为 j 的背包中的最大价值。
  2. 状态转移

    • 对于第 i 个物品,有两种选择:放入背包或不放入背包。
    • 如果不放入背包,状态转移为 dp[i][j] = dp[i-1][j]
    • 如果放入背包,状态转移为 dp[i][j] = dp[i-1][j-w[i-1]] + v[i-1](前提是 j 的容量要大于等于 w[i-1])。
  3. 初始状态

    • 如果没有物品或背包容量为0,则 dp[0][j] = 0 对于所有 j
  4. 目标

    • dp[n][W],即前 n 个物品在容量为 W 的背包中的最大价值。
代码实现
def knapsack(W, weights, values):
    n = len(weights)
    dp = [[0] * (W + 1) for _ in range(n + 1)]
    
    for i in range(1, n + 1):
        for w in range(W + 1):
            if w >= weights[i-1]:
                dp[i][w] = max(dp[i-1][w], dp[i-1][w-weights[i-1]] + values[i-1])
            else:
                dp[i][w] = dp[i-1][w]
    
    return dp[n][W]

# 示例输入
W = 50
weights = [10, 20, 30]
values = [60, 100, 120]

# 调用函数
print(knapsack(W, weights, values))  # 输出 220

逐步解释

  1. 定义状态

    • dp[i][j]:考虑前 i 个物品,放入容量为 j 的背包中的最大价值。
  2. 状态转移

    • 不选第 i 个物品dp[i][j] = dp[i-1][j]
    • 选第 i 个物品dp[i][j] = dp[i-1][j-w[i-1]] + v[i-1](如果容量允许)
  3. 初始化

    • dp[0][j] = 0,因为没有物品时,背包中的价值为 0。
  4. 求解

    • 通过双重循环,逐步更新 dp 数组,最终 dp[n][W] 就是所求的最大值。

更复杂的例子:带附件的背包问题

带附件的背包问题在普通背包问题的基础上增加了更多的组合关系。对于这种问题,我们可以用类似的方法来理解和构建递推式。

  1. 状态定义

    • dp[i][j] 表示前 i 个主件及其附件在容量为 j 的背包中的最大价值。
  2. 状态转移

    • 类似于普通背包问题,但需要考虑主件和附件的组合关系。
  3. 初始化

    • dp[0][j] = 0,没有主件和附件时,背包中的价值为 0。
  4. 求解

    • 通过双重循环,逐步更新 dp 数组,最终 dp[m][n] 就是所求的最大值。
代码实现(带附件的背包问题)

基于你提供的代码,可以进行逐步讲解和理解:

n, m = map(int, input().split())  # 读取背包容量和物品总数
primary, annex = {}, {}  # 用于存储主件和附件

# 读取物品信息并分类存储
for i in range(1, m + 1):
    x, y, z = map(int, input().split())
    if z == 0:
        primary[i] = [x, y]
    else:
        if z in annex:
            annex[z].append([x, y])
        else:
            annex[z] = [[x, y]]

# 主件的个数
m = len(primary)

# 初始化动态规划表
dp = [[0] * (n + 1) for _ in range(m + 1)]

# 用于存储主件及其附件组合的价格和价值
w, v = [[]], [[]]

# 遍历所有主件
for key in primary:
    w_temp, v_temp = [], []
    w_temp.append(primary[key][0])  # 仅主件
    v_temp.append(primary[key][0] * primary[key][1])
    
    if key in annex:  # 如果有附件
        w_temp.append(w_temp[0] + annex[key][0][0])  # 主件 + 附件1
        v_temp.append(v_temp[0] + annex[key][0][0] * annex[key][0][1])
        if len(annex[key]) > 1:  # 如果有第二个附件
            w_temp.append(w_temp[0] + annex[key][1][0])  # 主件 + 附件2
            v_temp.append(v_temp[0] + annex[key][1][0] * annex[key][1][1])
            w_temp.append(w_temp[0] + annex[key][0][0] + annex[key][1][0])  # 主件 + 附件1 + 附件2
            v_temp.append(v_temp[0] + annex[key][0][0] * annex[key][0][1] + annex[key][1][0] * annex[key][1][1])
    
    w.append(w_temp)
    v.append(v_temp)

# 动态规划求解
for i in range(1, m + 1):
    for j in range(10, n + 1, 10):
        max_i = dp[i - 1][j]
        for k in range(len(w[i])):
            if j - w[i][k] >= 0:
                max_i = max(max_i, dp[i - 1][j - w[i][k]] + v[i][k])
        dp[i][j] = max_i

# 输出结果
print(dp[m][n])

总结

  1. 明确状态定义:清楚地定义 dp[i][j] 的含义。
  2. 找出状态转移方程:根据问题的特点,找出递推关系。
  3. 初始化状态:设定初始条件。
  4. 迭代计算:通过循环或递归计算出结果。
  5. 读取结果:最终结果通常是 dp 表中的某个值。

通过练习和不断总结,你会逐渐掌握构建递推式和解决动态规划问题的方法。
要理解和构建递推式如 dp[i][j] = dp[i-1][j-w[i-1]] + v[i-1] 这样的动态规划转移方程,并不是靠灵光一现的“神仙想法”,而是通过系统的思考和不断练习来掌握的。以下是一些步骤和方法,可以帮助你逐步培养这种思维方式:

1. 理解问题

首先,明确问题的具体要求和限制条件。例如,在背包问题中,目标是最大化价值,而限制条件是背包容量。

2. 定义状态

确定什么是“状态”,即问题在每一步的具体情况。例如:

  • dp[i][j] 表示前 i 个物品中,放入容量为 j 的背包的最大价值。

3. 基本思路

在构建递推式时,考虑每一步的选择:

  • 如果当前物品不放入背包,那么 dp[i][j] 就等于前 i-1 个物品在容量为 j 时的最大价值,即 dp[i][j] = dp[i-1][j]
  • 如果当前物品放入背包,那么要考虑容量 j 减去当前物品的重量后,前 i-1 个物品的最大价值,再加上当前物品的价值。

4. 状态转移方程

结合基本思路,状态转移方程自然就出来了:

[ dp[i][j] = \max(dp[i-1][j], dp[i-1][j-w[i-1]] + v[i-1]) ]

这表示我们要在不选当前物品和选当前物品两种情况中选择一个最大值。

5. 初始化

确定初始状态,比如在没有物品或背包容量为零的情况下,价值都是零:

[ dp[0][j] = 0 ]

6. 目标

确定最终需要的结果,通常是 dp 表中的某个值,比如 dp[n][W],表示考虑所有物品,在容量为 W 的背包中的最大价值。

7. 举例推导

通过举例和手动推导小规模实例,验证状态转移方程是否正确。例如:

  • 有 3 个物品,重量和价值分别为 (10, 60), (20, 100), (30, 120),背包容量为 50。

用表格列出每一步的 dp 值,逐步计算,直观理解状态转移。

8. 练习更多问题

解决更多不同类型的动态规划问题,培养动态规划思维:

  • 最长公共子序列
  • 最小路径和
  • 硬币找零
  • 戳气球等

示例讲解

用上述步骤具体演示一个例子:0/1 背包问题

示例问题
  • 背包容量:50
  • 物品:3 个
    • 重量:10, 20, 30
    • 价值:60, 100, 120
初始化
n = 3  # 物品个数
W = 50  # 背包容量
weights = [10, 20, 30]
values = [60, 100, 120]
dp = [[0] * (W + 1) for _ in range(n + 1)]  # 初始化 DP 表
状态转移方程
for i in range(1, n + 1):
    for j in range(W + 1):
        if j >= weights[i-1]:
            dp[i][j] = max(dp[i-1][j], dp[i-1][j-weights[i-1]] + values[i-1])
        else:
            dp[i][j] = dp[i-1][j]
结果
print(dp[n][W])  # 输出结果:220
手动推导

考虑第 1 个物品(重量 10,价值 60):

  • 背包容量 0-9:无法放入,dp[1][j] = 0
  • 背包容量 10-50:放入后,dp[1][j] = 60

考虑第 2 个物品(重量 20,价值 100):

  • 背包容量 0-19:无法放入,dp[2][j] = dp[1][j]
  • 背包容量 20-50:放入后,dp[2][j] = max(dp[1][j], dp[1][j-20] + 100)

最终:

考虑第 3 个物品(重量 30,价值 120):

  • 背包容量 0-29:无法放入,dp[3][j] = dp[2][j]
  • 背包容量 30-50:放入后,dp[3][j] = max(dp[2][j], dp[2][j-30] + 120)

这样逐步推导,验证递推式的正确性。

总结

掌握动态规划问题的递推式构建,不是靠突发奇想,而是通过理解问题、定义状态、构建状态转移方程、验证和练习来逐步培养的。希望通过这些方法和步骤,你能逐步掌握和运用动态规划解决问题。

标签:背包,temp,annex,问题,key,物品,浅析,dp
From: https://blog.csdn.net/hackermengzhi/article/details/139348079

相关文章

  • 背包九讲--阅读笔记
    背包九讲很古老的文章,from2007,比我年龄都大。但是确实写得很好。01背包很基础。设\(f[i][j]\)为考虑前\(i\)个物品,背包容量为\(j\)的最大价值。$f[i][j]=max{f[i-1][j],f[i-1][j-w[i]]+c[i]}$考虑可以滚动数组,但更高妙的,是倒序枚举\(j\),即:$f......
  • MySQL 中 不等于 会过滤掉 Null 的问题
    在写SQL条件语句时经常用到不等于 != 的筛选条件。  此时要注意此条件会将字段为 Null 的数据也当做满足不等于的条件而将数据筛选掉。例:表AA1B110213Null执行如下查询:SELECT*FROMAWHEREB1!=1;得到的结果如下:A1B110第三......
  • MATLAB求解混合整数线性规划问题(MILP)
    文章目录前言一、混合整数线性规划模型(MILP)的概念二、matlab求解方法1.求解说明2.求解代码总结前言本文主要介绍混合整数线性规划模型(MILP)的概念及利用matlab进行求解。一、混合整数线性规划模型(MILP)的概念线性规划模型(LinearProgramming,LP):LP的定义比较简单......
  • 一文看懂企业HPC环境下数据传输常见问题及解决方案
    HPC通常指的是“高性能计算”(High-PerformanceComputing)。高性能计算是计算机科学的一个分支,专注于构建和使用能够执行计算密集型任务(如模拟、数据分析、可视化等)的计算机系统。这些系统通常包括多个处理器(CPU)、图形处理器(GPU)、专用加速器或其他类型的计算单元,它们通过网络连接......
  • 基于深度神经网络的人脸识别相关问题
    基于深度神经网络的人脸识别相关问题1、DNN与CNN的关系CNN可以看作是一种特殊的DNN,它们之间的关系是包含和被包含的关系。CNN的核心是卷积层,该层可以有效地识别图像中的局部模式,并使用池化层来减少特征映射的维度。此外,CNN还包括其他类型的层,例如全连接层和激活函数层,用于将卷......
  • 解决删除文件后 WSL2 磁盘空间不释放的问题
    Tags:#wsl#wsl2#windows今天突然发现 C 盘快满了,想起来之前把 Docker 容器的数据持久化到了 WSL2 的某个目录下,于是就想着把不需要的文件清理了。但清理完毕之后我发现 C 盘的剩余空间并没有变大,非常的奇怪。后来我在网上搜索了很久,终于找到了原因和解决方法。1分析......
  • Cesium 中 GeoJsonDataSource 贴地不生效的问题
    Cesium中GeoJsonDataSource可以设置clampToGround为true来确保其贴地,但有时会出现不生效的情况。可能有以下几个原因:数据源不是地理坐标系(WGS84):如果数据源不是基于WGS84坐标系的,则可能无法正确地将图形贴到地球表面。确保你的数据源使用正确的坐标系。数据源中的图形高......
  • Golang GRPC 环境 问题
    生成文件执行protoc--go_out=.--go_opt=paths=source_relative--go-grpc_out=.--go-grpc_opt=paths=source_relativeservice.proto 报下列错处理方法1.'protoc-gen-go'不是内部或外部命令,也不是可运行的程序或批处理文件。a.检查gopath目录(%GOPATH%\)的bin文件夹,是否有......
  • mac(m1 pro芯片)上解决安装Lightgbm库失败问题
    报错日志执行pipinstalllightgbm时报错:CouldnotfindcompilersetinenvironmentvariableCC:gcc-8.CallStack(mostrecentcallfirst):CMakeLists.txt:32(project)CMakeError:CMAKE_C_COMPILERnotset,afterEnableLanguage......
  • 知识点整理 - 连通性相关 | 《综述图论中连通性及相关问题的一些处理方法》学习笔记
    是ix35老师论文的学习笔记,同时也用作连通性相关知识梳理。可能不会包含很多定义,只会挑重要的写。会包含一些例题。定义与记号\(u\rightsquigarrowv\)代表\(u\)到\(v\)的一条路径。有时也会用这个记号表示连通性。无向图点/边连通度:若\(u,v\)任意点割集大小不小......