1. 0-1背包问题

1.1 题目描述

有一个包和n个物品,包的容量为m,每个物品都有各自的体积和价值,问当从这n个物品中选择多个物品放在包里而物品体积总数不超过包的容量m时,能够得到的最大价值是多少?[对于每个物品不可以取多次,最多只能取一次,之所以叫做0-1背包,0表示不取,1表示取]

1.2思路

动态规划,根据动态规划解题步骤(建立模型、寻找约束条件、找子问题之间的的递推关系式、填表(区间模型)、得到解)找出0-1背包问题的最优解以及解组成。

1.3 步骤

首先定义:Vi表示标号为i物品的价值,Wi表示标号为i物品的体积。capacity为背包的最大容量。那么一个0-1背包问题的具体步骤如下:

  1. 建立模型,我们要求在物品总体积限制为capacity的情况下,尽可能装下价值高的物品组合,其模型如下:
  2. 寻找状态转换方程,即子问题之间的递推关系,我们令V(i,j)表示当前背包为容量 j,前 i 个物品最佳组合对应的价值最优解,那么就有两种可能:
    a)包的容量比该商品体积小,即j < Vi,装不下第i个物品。此时背包容量为j,前i个物品的最优解与背包容量为j,前i-1个物品最佳组合对应的最优解是一样的,即V(i,j)=V(i-1,j)
    b)当包的容量等于或大于该商品的体积,即j >= Vi,能装下第i个物品。那无非有两种情况,在前i个物品,背包容量为j的最优组合里有第i个物品,但同时占据了背包Wj个容量,V(i,j) = V(i-1,j-Wi)+Vi;在前i个物品,背包容量为j的最优组合里没有第i个物品,此时V(i,j) = V(i-1,j)。所以,根据最优性原理,背包容量为j,前i个物品的最优解V(i,j) 要取这两种情况的最大值,V(i,j) = max{V(i-1,j),V(i-1,j-Wi)+Vi},
由此可以得出递推关系式:


3. 根据递推公式进行逐行填表,初始化表V(i,o) = 0,V(0,j) = 0,表的列维度从0到n,行维度从0到capacity。
4. 根据填表过程,编写代码。

1.3 举例

有如下几种物品,物品的价值和体积如表所示:

首先定义:Vi表示标号为i物品的价值,Wi表示标号为i物品的体积。另外背包的总体积为10。

1.3.1 填表

填表过程如下:

1.4 python实现

def  zeroOneBag(num,capacity,weightList,valueList):valueExcel= [[0 for j in range(capacity + 1)] for i in range(num + 1)]for i in range(1,num+1):for j in range(1,capacity+1):valueExcel[i][j] = valueExcel[i - 1][j]if j >= weightList[i-1] and valueExcel[i][j] < (valueExcel[i - 1][j - weightList[i - 1]] + valueList[i - 1]):valueExcel[i][j] = (valueExcel[i - 1][j - weightList[i - 1]] + valueList[i - 1])return valueExcel
print(zeroOneBag(5,10,[2,2,6,5,4],[6,3,5,4,6]))
# 输出结果为:
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6],
[0, 0, 6, 6, 9, 9, 9, 9, 9, 9, 9],
[0, 0, 6, 6, 9, 9, 9, 9, 11, 11, 14],
[0, 0, 6, 6, 9, 9, 9, 10, 11, 13, 14],
[0, 0, 6, 6, 9, 9, 12, 12, 15, 15, 15]]

因此该问题的价值最大组合为15。

1.4.1 算法优化

上面这种写法的时间复杂度为o(nc),空间复杂度也为o(nc)。时间复杂度不能在优化了,但是空间复杂度可以进行优化,使之降低到o(c)。

def zeroOneBagOpt(num,capacity,weightList,valueList):valueRes = [0 for i in range(capacity+1)]for i in range(1, num + 1):for j in range(capacity, 0, -1):if j >= weightList[i-1]:valueRes[j] = max(valueRes[j-weightList[i-1]]+valueList[i-1], valueRes[j])# i变一次,数组的更新一次,利用数组的不断更新,达到上面的方法动态规划的目的。# 当j遍历更新完一遍valueRes,valueRes就相当于完成上边方法中表第i+1行的填充.# 必须从后向前遍历和判断储存结果的数组,因为valueRes[j-weightList[i-1]]+valueList[i-1]就相当于V(i-1,j-Wi)+ Vi# 判断第i个物品是否是最优解的时候,需要以i-1的情况为基础,不能掺杂有关第i个物品的信息,# 如果从前往后遍历,那么valueRes[:j]的数是判断了i之后的,会产生错误。# 这里的value[j]即为上一次最佳结果,从后向前可以在遍历到j的未知的时候,不破坏j前面的上一次的最佳结果。# return valueRes
print(zeroOneBagOpt(5,10,[2,2,6,5,4],[6,3,5,4,6]))
# 输出结果为:[0, 0, 6, 6, 9, 9, 12, 12, 15, 15, 15]

1.4.2 找到最佳组合的构成

通过上面的方法可以求出背包问题的最优解,但还不知道这个最优解由哪些商品组成,故要根据最优解回溯找出解的组成,根据填表的原理可以有如下的寻解方式:

  1. V(i,j)=V(i-1,j)时,说明没有选择第i 个商品,则回到V(i-1,j);
  2. V(i,j)!=V(i-1,j)时,说明装了第i个商品,该商品是最优解组成的一部分,一定有V(i,j)=V(i-1,j-w(i))+v(i),随后我们得回到装该商品之前,即回到V(i-1,j-w(i));
  3. 重复1,2,一直遍历到i=0结束为止,所有解的组成都会找到。

    最优解的组成由第1个,第2个,第5个。
def showRes(num,capacity,weightList,valueExcel):indexRes = []j = capacityfor i in range(num,0,-1):if valueExcel[i][j] != valueExcel[i-1][j]:indexRes.append(i)j -= weightList[i-1]return indexRes
valueExcel = (zeroOneBag(5,10,[2,2,6,5,4],[6,3,5,4,6]))
print(showRes(5,10,[2,2,6,5,4],valueExcel))
# 输出结果为:[5, 2, 1]

2. 多重背包问题

2.1 问题详解

2.2 题目思路

2.2.1 一般思路


例如,第i种物品有3个,因此可以把这三个物品进行拆分,看作是不同种类的物体,不过是具有相同体积和价值的不同物品,通过拆分,就把可以放多个同种物品拆分成只能放0个或者一个“不同”物品的0-1背包问题。

2.2.1 python代码

# weightList:每种物品的体积
# valueList:每种物品的价值
# numList:每种物品的数量
def trans(weightList,valueList,numList):      # 转换函数 K = len(numList)newWeightList = []newValueList = []num = sum(numList)for i in range(K):for j in range(numList[i]):newWeightList.append(weightList[i])newValueList.append(valueList[i])return num,newValueList,newWeightList
def zeroOneBag(numList,capacity,weightList,valueList):# 先将多重问题转成0-1问题,利用0-1问题的代码。num,newValueList,newWeightList = trans(weightList,valueList,numList)valueExcel= [[0 for j in range(capacity + 1)] for i in range(num + 1)]for i in range(1,num+1):for j in range(1,capacity+1):valueExcel[i][j] = valueExcel[i - 1][j]if j >= newWeightList[i-1] and valueExcel[i][j] < (valueExcel[i - 1][j - newWeightList[i - 1]] + newValueList[i - 1]):valueExcel[i][j] = (valueExcel[i - 1][j - newWeightList[i - 1]] + newValueList[i - 1])return valueExcel
print(zeroOneBag([2,5,10],8,[2,2,1],[20,10,6]))
# 输出结果:
#[[0, 0, 0, 0, 0, 0, 0, 0, 0],
# [0, 0, 20, 20, 20, 20, 20, 20, 20],
# [0, 0, 20, 20, 40, 40, 40, 40, 40],
# [0, 0, 20, 20, 40, 40, 50, 50, 50],
# [0, 0, 20, 20, 40, 40, 50, 50, 60],
# [0, 0, 20, 20, 40, 40, 50, 50, 60],
# [0, 0, 20, 20, 40, 40, 50, 50, 60],
# [0, 0, 20, 20, 40, 40, 50, 50, 60],
# [0, 6, 20, 26, 40, 46, 50, 56, 60],
# [0, 6, 20, 26, 40, 46, 52, 56, 62],
# [0, 6, 20, 26, 40, 46, 52, 58, 62],
# [0, 6, 20, 26, 40, 46, 52, 58, 64],
# [0, 6, 20, 26, 40, 46, 52, 58, 64],
# [0, 6, 20, 26, 40, 46, 52, 58, 64],
# [0, 6, 20, 26, 40, 46, 52, 58, 64],
# [0, 6, 20, 26, 40, 46, 52, 58, 64],
# [0, 6, 20, 26, 40, 46, 52, 58, 64],
# [0, 6, 20, 26, 40, 46, 52, 58, 64]]

最终的最大价值为64。和0-1背包问题一样,可以对空间进行优化,这里就省略了。

2.2.2 优化

利用一般的思路讲多重背包问题转化成0-1背包问题进行解决,某种物品有多少个就要拆分成多少个不同的物品,这会增加时间复杂度和空间复杂度。因此我们可以利用另一种分解方法:二进制分解

二进制分解代码
def binaryDecomposition(n):  # 二进制分解k = 0res = []while n - 2**(k+1) + 1  > 0:res.append(2**k)k += 1res.append(n-2 ** (k) + 1)return res

如果第i种物品有13个,根据这种思路,13拆分成1,2,4,6。只需要将这种物品拆分成4个物品,这四个物品的体积和价值分别是(Wi,Vi),(2Wi,2Vi),(4Wi,4Vi),(6Wi,6Vi),而不是13个物品,这就降低了时间复杂度和空间复杂度。

2.2.2 python代码

# optimization
def binaryDecomposition(n):  # 二进制分解k = 0res = []while n - 2**(k+1) + 1  > 0:res.append(2**k)k += 1res.append(n-2 ** (k) + 1)return res
def trans(weightList,valueList,numList):newWeightList = []newValueList = []for i in range(len(numList)):for j in binaryDecomposition(numList[i]):newWeightList.append(j * weightList[i])newValueList.append(j * valueList[i])num = len(newValueList)return num,newValueList,newWeightList
def  zeroOneBag(numList,capacity,weightList,valueList):num,newValueList,newWeightList = trans(weightList,valueList,numList)valueExcel= [[0 for j in range(capacity + 1)] for i in range(num + 1)]for i in range(1,num+1):for j in range(1,capacity+1):valueExcel[i][j] = valueExcel[i - 1][j]if j >= newWeightList[i-1] and valueExcel[i][j] < (valueExcel[i - 1][j - newWeightList[i - 1]] + newValueList[i - 1]):valueExcel[i][j] = (valueExcel[i - 1][j - newWeightList[i - 1]] + newValueList[i - 1])return valueExcel
print(zeroOneBag([2,5,10],8,[2,2,1],[20,10,6]))
# 输出结果为:
[[0, 0, 0, 0, 0, 0, 0, 0, 0],
#[0, 0, 20, 20, 20, 20, 20, 20, 20],
#[0, 0, 20, 20, 40, 40, 40, 40, 40],
#[0, 0, 20, 20, 40, 40, 50, 50, 50],
#[0, 0, 20, 20, 40, 40, 50, 50, 60],
#[0, 0, 20, 20, 40, 40, 50, 50, 60],
#[0, 6, 20, 26, 40, 46, 50, 56, 60],
#[0, 6, 20, 26, 40, 46, 52, 58, 62],
#[0, 6, 20, 26, 40, 46, 52, 58, 64],
#[0, 6, 20, 26, 40, 46, 52, 58, 64]]

通过输出结果也可以看出,表的维度比上一种方法要低。最终的最佳组合的价值为64。

3.完全背包问题

3.1 问题描述

3.2.1 思路

完全背包问题和多重背包问题以及0-1背包问题的唯一不同就在于在背包容量允许的情况下,可以在背包里放无限个同一种物品。
下面是0-1背包问题的递推公式:

我们仍然令V(i,j)表示当前背包为容量 j,前 i 个物品最佳组合对应的价值最优解。那么我们可以根据这个0-1背包问题的状态转换方程通过变化,得到完全背包问题的递推公式。

3.2 python代码

def compKnap(num,capacity,weightList,valueList):valueExcel = [[0 for j in range(capacity + 1)] for i in range(num + 1)]for i in range(1, num + 1):for j in range(1, capacity + 1):for k in range((j // weightList[i - 1]) + 1):if valueExcel[i][j] < (valueExcel[i - 1][j - k*weightList[i - 1]] + k*valueList[i - 1]):valueExcel[i][j] = (valueExcel[i - 1][j - k * weightList[i - 1]] + k*valueList[i - 1])return valueExcel
# print(compKnap(5,16,[5,4,7,2,6],[12,3,10,3,6]))
"""
输出结果为:
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 24, 24, 24, 24, 24, 36, 36],[0, 0, 0, 0, 3, 12, 12, 12, 12, 15, 24, 24, 24, 24, 27, 36, 36],[0, 0, 0, 0, 3, 12, 12, 12, 12, 15, 24, 24, 24, 24, 27, 36, 36],[0, 0, 3, 3, 6, 12, 12, 15, 15, 18, 24, 24, 27, 27, 30, 36, 36],[0, 0, 3, 3, 6, 12, 12, 15, 15, 18, 24, 24, 27, 27, 30, 36, 36]]
"""

3.2.2 优化

和0-1背包问题一样,完全背包问题也可以通过二进制分解来进行拆分,来降低时间复杂度和空间复杂度。同时,还可以优化,将其优化成和相同条件下0-1背包问题时间复杂度、空间复杂度一样的算法。
其方法和0-1背包问题的优化方法一样。都是借助一个一维数组。

3.2.2 python 代码

def compKnapOpt(num,capacity,weightList,valueList):valueExcel = [0 for j in range(capacity + 1)]for i in range(1, num + 1):for j in range(1, capacity + 1):if weightList[i-1]<=j:valueExcel[j] = max(valueExcel[j - weightList[i - 1]] + valueList[i - 1], valueExcel[j])return valueExcel
print(compKnapOpt(5,16,[5,4,7,2,6],[12,3,10,3,6]))
# 输出结果:
[0, 0, 3, 3, 6, 12, 12, 15, 15, 18, 24, 24, 27, 27, 30, 36, 36]

背包问题(01背包问题,多重背包问题,完全背包问题)——基于python的动态规划相关推荐

  1. 代码随想录算法训练营第四十六天|139.单词拆分、多重背包、背包问题总结篇

    一·.单词划分 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词. 分析如下: 动规五部曲分析如下: 1.确定dp数组以及 ...

  2. 背包问题——01背包

    背包问题--01背包 01背包作为动态规划(dynamic programing)中最基础的问题,需要我们彻底理解其中的原理,为以后解决更难的动态规划问题打下良好的基础. 这里拟定一个01背包问题: ...

  3. 动态规划之背包问题——01背包

    算法相关数据结构总结: 序号 数据结构 文章 1 动态规划 动态规划之背包问题--01背包 动态规划之背包问题--完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 ...

  4. C++ 背包问题——01背包

    由于编辑器原因,01背包文章搬家了,想看到更好的01背包问题题解,请点击链接: C++背包问题--01背包_小天狼星_布莱克的博客-CSDN博客

  5. [AcWing] 9. 分组背包问题(C++实现)分组背包问题模板题

    [AcWing] 9. 分组背包问题(C++实现)分组背包问题模板题 1. 题目 2. 读题(需要重点注意的东西) 3. 解法 4. 可能有帮助的前置习题 5. 所用到的数据结构与算法思想 6. 总结 ...

  6. 多重背包单调队列优化思路_动态规划入门——多重背包与单调优化

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法与数据结构的第14篇文章,也是动态规划专题的第三篇. 在之前的文章当中,我们介绍了多重背包的二进制拆分的解法.在大多数情况下,这种 ...

  7. 基于Python的线性回归预测模型介绍及实践

    基于Python的线性回归预测模型介绍及实践 这是一篇学习的总结笔记 参考自<从零开始学数据分析与挖掘> [中]刘顺祥 著 完整代码及实践所用数据集等资料放置于:Github 线性回归预测 ...

  8. 「实战案例」基于Python语言开发的信用评分卡

    信用风险计量模型可以包括跟个人信用评级,企业信用评级和国家信用评级.人信用评级有一系列评级模型组成,常见是A卡(申请评分卡).B卡(行为模型).C卡(催收模型)和F卡(反欺诈模型). 今天我们展示的是 ...

  9. python深度神经网络量化_基于Python建立深度神经网络!你学会了嘛?

    原标题:基于Python建立深度神经网络!你学会了嘛? 图1 神经网络构造的例子(符号说明:上标[l]表示与第l层:上标(i)表示第i个例子:下标i表示矢量第i项) 单层神经网络 图2 单层神经网络示 ...

最新文章

  1. HDU2853(最大权完美匹配)
  2. JavaScript实现存储HTML字符串
  3. IIS与COM组件权限的问题
  4. wxpython安装_01Python安装教程与特色介绍
  5. jquery-1.10.2 获取checkbox的checked属性总是undefined
  6. 4:springApplication.run 原理
  7. Java黑皮书课后题第7章:7.27(相同的数组)如果两个数组list1和list2的内容相同,认为相同(不是完全相同)。编写一个测试程序,提示用户输入两个整数列表,然后显示这两个列表是否相同
  8. 惊!空 struct 地址竟然不相等
  9. 【STM32】STM32CubeMX教程二--基本使用(新建工程点亮LED灯)
  10. LightOJ 1319 Monkey Tradition(中国剩余定理)
  11. 分享一个开源的项目,数据结构和算法必知必会的50个代码实现
  12. html登录页面代码实现原理,web登录代码
  13. 【绝密外泄】风哥Oracle数据库DBA高级工程师培训视频教程与内部资料v0.1
  14. 轻松实现微信、QQ防撤回
  15. linux中命令tat,文件管理类命令(ls,tat,glob,cp,touch等)
  16. HQChart使用教程95-报价列表对接第3方数据3-股票数据
  17. NFTScan 开发者平台推出多链 NFT 数据 Pro API 服务
  18. 计算机科学与技术没落,这七所985一个A+学科都没有评上?有点尴尬了
  19. 4月全球“.网址”域名总量排行榜:ZDNS份额仍超99%
  20. 中高管职业生涯的挑战与机遇:如何在“中年危机”中突围?

热门文章

  1. 复习Java.Lang包Java面试题Vector、ArrayList、LinkedList区别生活【记录一个咸鱼大学生三个月的奋进生活】007
  2. idea 一行js_IntelliJ IDEA使用之JavaScript
  3. ant-design for react 日期选择器遇到的问题。(mars3d开发)
  4. js实现折线、柱形、散点、扇形统计图之间的转换
  5. Spring二级缓存
  6. Windows Practice_文件_文件分割器(二)
  7. Handlebars总结
  8. 选择仓储货架时需要注意些什么?又如何预防仓储货架的倒塌?
  9. 【前端职业规划思考】
  10. 2014年07月28日 国产电影的春天吗