初步了解01背包问题(分治篇)

Kubrnete
• 阅读 2444

目录

问题描述

输入格式

输出格式

基于0/1背包的迭代算法

0/1背包问题的分析

分治法

总结


问题描述

Coda非常喜欢玩“NewWorld Online”,受到某部动画的影响,他决定创建名为“梅普露”的角色,并把所有技能点都加到防御力上。Coda发现了一个包含了n行数据的列表,表上每行有一组数据,包含两个整数aibi,决定了第i 次点技能时消耗ai 点技能点,提升bi 点属性值。

Coda在加点时可以从列表中选择喜欢的一组数据作为加点时的消耗和提升,但是每组数据使用过一次就不能再次使用了。

Coda现在拥有p个技能点,初始防御力为d,请问如果Coda把这些技能点全拿来加防御力,最高能到达多少防御力?

输入格式

第一行包含3个整数: n,p,d,分别表示列表中数据组数、拥有的技能点数和初始防御力。 接下来n行,每行包含2个整数aibi,分别表示列表中各组数据,ai 是加点时技能点的消耗,bi 是加点时防御力的提升。

输出格式

一行,包含一个整数,最大可以达到的防御力。

初步了解01背包问题(分治篇))初步了解01背包问题(分治篇)

(如果想要直接看动态规划部分,请直接点击动态规划篇前往下一篇文章)


这是一个经典的【01背包】问题:

有N个物品,它们有各自的体积wi 和价值vi,现有给定容量C的背包,如何让背包里装入的物品具有最大的价值总和?

贪心的尝试

上一篇文章中,我们提到一种直接选择当前最优解的贪心算法。现在,让我们开启贪婪模式——既要装得多,又要价值高,那么就选性价比最高的好了——按照物品单价从大到小来装。 这可行吗?

在重量相同的情况下,我们总会选择对价值贡献最大的物品。然而,我们并不能确定这个贪心策略能否把背包装满。

我们只是按照了单价更高的策略依次对物品作选择。但物品不可分割。因此这个贪心策略只是解决了部分背包问题,在这里不能成立。我们很容易能够给出反例,当所有的物品单价相同,又或者:

N=3,C=30

物品:A B C

重量:20 15 15

价值:30 20 20

让我们理解一下贪心算法的适用情况:

  1. 因为贪心策略是由上一步的最优解推导下一步的最优解,而上一部之前的最优解则不作保留,并且,每一步的最优解一定包含上一步的最优解。在贪心算法中,作出的每步贪心决策都无法改变。
  2. 所谓贪心选择性质,是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。换句话说,当考虑做何种选择的时候,我们只考虑对当前问题最佳的选择而不考虑子问题的结果。这是贪心算法可行的第一个基本要素。贪心算法以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。
  3. 对于一个具体问题,要确定它是否具有贪心选择性质,每一步的最优解一定包含上一步的最优解。则必须证明每一步所作的贪心选择最终导致问题的整体最优解。

这可咋办呢?贪心用不了,dp又不会,只能用暴力天天TLE才能水水题这样子。

基于01背包的迭代算法

01背包问题的分析

01背包问题,顾名思义,是0/1的规划问题。由于物品不可分割且只能拿一次,于是对于每个物品只有两个选择:盘它或者不盘。

我们将选记为1,不选记为0。盘还是不盘?这是个问题。

下面我们以本题的输入样例作为例子(始值改为3 3 0):

初步了解01背包问题(分治篇))初步了解01背包问题(分治篇)

假定背包容量c=3,物品数n=3,它们的价值(v)和重量(w)如上图。现在要从中选择物品装入背包中,要求物品的重量不能超过背包的容量,并且最后放在背包中物品的总价值最大。

对于物品1,背包的当前容量为3>1,因此有两种选择。当我们选择一个物品的时候,背包的容量会减少,但是里面的物品总价值会增加。就像下面这样:

初步了解01背包问题(分治篇))初步了解01背包问题(分治篇)

这样就分出了两种情况,我们继续进行选择。如果我们选择了物品1,那么对于物品2,当前剩余容量为2,也可以选或者不选。可见,我们每次对物品做出一次选择,上面的一种可能就会分裂成两种。让我们继续画下去,最终就会形成我们的决策树。

初步了解01背包问题(分治篇))初步了解01背包问题(分治篇)

最后,我们在这棵决策树上(每个被圈中的方框都是待选的结果)通过比较得到最大值5。

(注:图中本应有9个待选结果,由于有四个容量不足,无法继续分裂更多可能)

值得说明的是,通常在问题中会更有更多复杂的用例,建议读者可以仿着画一次图,以便理解。

分治法

接下来,我们将问题扩展到一般情况。为了实现这个目的,我们需要将问题进行抽象并建模,然后将其划分为更小的子问题,找出递推关系式。

  1. 问题抽象:我们对第 i 个物品的选择定义为 xi(选为1,不选为0),vi代表第i个珠宝的价值,wi代表第i个珠宝的重量。
  2. 约束条件:x1w1 + x2w2 + x3w3 + ... + xnwn < c。对于上面的例子,则为1x1+2x2+3*x3<=3
  3. 建模:题目要求找出x1v1 + x2v2 + x3v3 + ... + xnvn 中的最大值
  4. 定义函数:V_total(i,j):表示当前背包剩余容量为j 时,前i 个物品最佳组合所对应的价值。

需要可以说明的是:原问题是要求出【**将n件物品放入容量为c的背包的最优价值】;**

现在,我们来考虑一下子问题:

我们将前*i* 件物品放入容量为*j* 的背包,所得到的最优价值为V_total(i,j)。如果只考虑第i件物品放还是不放,那么就可以转化为一个只涉及到前i-1个物品的问题

如果不选第i个物品,那么问题就转化为“前i-1\件物品放入容量为j的背包中的最优价值组合”,对应的值为V_total(i-1,j)。如果放第i个物品,那么问题就转化成了“前i-1件物品放入容量为j-wi的背包中的最优价值组合”,对应的值为V_total(i-1,j-wi)+vi

为避免看不懂,这里再次放出这张决策图。(建议自己另画一张进行分析)。

初步了解01背包问题(分治篇))初步了解01背包问题(分治篇)

怎么判断一个物品选还是不选呢?经过了上述分析,我们知道对于第i 个物品,我们有以下两种情况:

  • ①若背包剩余容量不足以容纳这个物品,则背包的价值与前i-1个物品的价值相同,即V_total(i,j) = V_total(i-1,j)
  • ②若背包剩余容量足以装下物品,则判断选或者不选背包中物品价值的大小,选出较大的一个。

​ 即Max{V_total(i-1,j-wi) + vi, V_total(i-1,j)}

于是有如下的递推关系:

初步了解01背包问题(分治篇))初步了解01背包问题(分治篇)

现在,我们可以照着这个递推关系,从i=n,j=c开始,写出递归解法的关键部分:

int V_total(int i,int j){//通过递归找出最优的组合
    if(j<w[i])
        return V_total(i - 1, j);
    else
        return max(V_total(i - 1, j), V_total(i - 1, j - w[i]) + v[i]);

}

初步了解01背包问题(分治篇)

没想到吧,竟然如此简单?(完整代码还要判断一下n==0或c==0的情况,由于重点不在这,就不附代码了)

总结

我们通过将大问题拆分成小问题,并寻找问题中的递推关系,解决一个个小问题,最终成功解决原问题。但由于分治法在子问题和子子...问题过程中进行了多次的重复计算(尝试每个组合的方法的问题是重复步骤,因为它存在重叠的子问题)如下图:

初步了解01背包问题(分治篇))初步了解01背包问题(分治篇)

因此这种方法具有重复的计算和时间复杂性是指数的。(指数级别是由树的深度depth决定的)

为了找到更好的解决方案,我们将在下一篇文章中学习动态规划算法。动态规划具有记忆性,一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。通过填写表把所有已经解决的子问题答案纪录下来,在新问题里需要用到的子问题可以直接提取,避免了重复计算,从而节约了时间。它试图仅对每个子问题解决一次,从而减少计算量。(所以在问题满足最优性原理(见下文)之后,用动态规划解决问题的核心就在于填表,表填写完毕,最优解也就找到。)这种做法在重复子问题的数目关于输入的规模呈指数增速时特别有用。

下面进入正题,请看下一篇文章:基于01背包问题的动态规划算法

本文转自 https://blog.csdn.net/guanguandaren/article/details/107940122,如有侵权,请联系删除。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Kubrnete Kubrnete
3年前
完全背包问题
问题描述有n种物品和一个容量为c的背包,每种物品都就可以选择任意多个,第i种物品的价值为v\i\,体积为w\i\,求解:在不超过背包容量的情况下,选择放入哪些物品能够使得背包中的价值最大?跟01背包一样,完全背包也是一个很经典的动态规划问题,不同的地方在于01背包问题中,每件物品最多选择一件;而在完全背包问题中,只要背包装得下,每件物品可
Stella981 Stella981
3年前
C# Aspose.Cells导出xlsx格式Excel,打开文件报“Excel 已完成文件级验证和修复。此工作簿的某些部分可能已被修复或丢弃”
报错信息:最近打开下载的Excel,会报如下错误。(xls格式不受影响)!(https://oscimg.oschina.net/oscnet/2b6f0c8d7f97368d095d9f0c96bcb36d410.png)!(https://oscimg.oschina.net/oscnet/fe1a8000d00cec3c
Stella981 Stella981
3年前
JS 苹果手机日期显示NaN问题
问题描述newDate("2019122910:30:00")在IOS下显示为NaN原因分析带的日期IOS下存在兼容问题解决方法字符串替换letdateStr"2019122910:30:00";datedateStr.repl
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Stella981 Stella981
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Easter79 Easter79
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Kubrnete
Kubrnete
Lv1
共看明月应垂泪,一夜乡心五处同。
文章
10
粉丝
1
获赞
6