回溯算法套路框架
淘宝搜:【天降红包222】领超级红包,京东搜:【天降红包222】
淘宝互助,淘宝双11微信互助群关注公众号 【淘姐妹】
0-1背包:有n个物品,第i个物品的体积为w[i],价值为v[i],每个物品至多选一个, 求体积和不超过capacity时的最大价值和,其中i从0开始。
- 递归函数定义:
- 状态转移方程:在递归过程中,我们需要依次考虑每个物品,然后采用算法套路十――回溯法之子集型回溯进行选或不选的枚举。
- 如果不选第i个物品,则为dfs(i-1, c),即前i-1个物品在背包容量为c的情况下能够获得的最大价值。
- 如果选第i个物品,则为dfs(i-1, c-w[i]) + v[i],即前i-1个物品在背包容量为c-w[i]的情况下能够获得的最大价值加上第i个物品的价值v[i]。
- 最终,dfs(i, c)的值为以上两种情况的较大值,即
- 边界条件:
- 当i小于0,没有物品选择,此时dfs(i, c)的值为0;或者判断当i==0时,如果背包容量c能够放下第一个物品,则直接返回第一个物品的价值v[0],否则返回0。
- 当背包的剩余容量c小于第i个物品的重量w[i]时,无法选择第i个物品,此时dfs(i, c)的值为dfs(i-1, c)。
- 所求值:dfs(n-1, capacity),表示考虑前n个物品,在背包容量为capacity的情况下能够获得的最大价值。
定义一个二维数组dp,其中,并使用一个双重循环来遍历所有的物品和背包容量,根据0/1背包问题的状态转移方程 dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]) if j>=w[i] else dp[i-1][j] 来更新dp数组的值。最终,dp[n-1][capacity]的值即为所求的最大价值。
考虑到数组从0开始,为了数组方便我们可以将代码中所有的i加一,此时状态转移方程为状态转移方程 dp[i+1][j] = max(dp[i][j], dp[i][j - w[i]] + v[i]) if j>=w[i] else dp[i+1][j] = dp[i ][j],这样最后返回结果为dp[n][capacity],如下所示
可以将二维数组dp[]转换为一维数组dp[],此时我们需要使用一个双重循环来遍历所有的物品和背包容量,根据0/1背包问题的状态转移方程来更新dp数组的值。 其实该思路就是算法套路十――回溯法之子集型回溯中的思路二――从答案出发的思路,,并用数组记录,因此 在一维数组下的状态转移方程中,。因为在状态转移时,对于当前正在考虑的第 i 件物品,假如其体积 w[i] 比当前枚举的体积 j 大,那么该物品就无法放入背包中,因此它应该不会对 f[j] 的结果产生影响。在一维数组中,我们并没有明确保存“选取了哪些物品”,而是只保存了阶段 i(前 i 个物品)和决策 j(背包容量)。在状态转移时,我们始终是以 “前i个物品” 作为暗含条件的。
。当我们从后往前遍历时,我们确保在计算 dp[j] 时,dp[j-w[i]] 的值是基于不包含当前物品 i 的子问题的解。这样,我们可以确保每个物品只被使用一次。 如果我们从前往后遍历,那么在计算 dp[j] 时,dp[j-w[i]] 的值可能已经包含了物品 i,这样就可能导致物品 i 被重复使用,从而得到错误的结果。
如物品重量:w = [3, 4, 5] 物品价值:v = [30, 50, 60] 背包容量:capacity = 8
首先,我们看一下遍历物品 0 时从前往后遍历的情况:
从前往后遍历时,我们可以看到 dp[6] 的值为 60,
现在,我们看一下遍历物品 0 时从后往前遍历的情况: 从后往前遍历时,我们可以看到 dp[6] 的值为 30,这是正确的,因为我们只使用了物品 0(重量为 3,价值为 30)一次。 通过这个对比,我们可以看到。
完全背包:N种物品和一个容量为V的背包,每种物品都有无限件可用。第i种物品的体积是v[i],价值是val[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。 0/1背包和完全背包是两种常见的背包问题,它们的主要区别在于每个物品是否可以重复使用。 在0/1背包问题中,每个物品只能使用一次,即要么选择将该物品放入背包中,要么不放入。因此,0/1背包问题也被称为“二进制背包问题”。而在完全背包问题中,每个物品可以使用多次,即可以选择将该物品放入背包中多次。因此,完全背包问题也被称为“多重背包问题”。
因为每个物品可以使用多次,所以在解决完全背包问题时,,故完全背包的状态转移方程:dp(i,j)=max(dp(i-1,j),dp(i,j-v)+val[i]),这也是与0/1背包的唯一区别,就是在选择物品i之后仍需继续递归物品i
同0/1背包定义一个二维数组dp,其中,并使用一个双重循环来遍历所有的物品和背包容量,转移方程 dp[i+1][j] = max(dp[i][j], dp[i+1][j-w[i]] + v[i]) if j>=w[i] else dp[i+1][j] = dp[i][j]来更新dp数组的值。最终,dp[n-1][capacity]的值即为所求的最大价值。
与0/1背包类似,完全背包也可以换为一维数组,不过不同的是。
- 至多装capacity,求方案数/最大价值和
- 恰好装capacity,求方案数/最大/最小价值和
- 至少装capacity,求方案数/最小价值和
最大、小值问题一般会回归到 方案数 或 数字个数问题, 一般会使用到 max/min 函数约束答案,此时使用inf 初始化来表示极端情况。 每种方式的数组初始及转移方程如何定义可参考下表,且对于每种问题为什么如此设计初始数组及转移方程经过思考相信可以理解,但做题时仍需具体问题具体分析
问题类型 | 二维初始数组 | 二维转移方程 | 最终结果 | 注意事项 | 一维初始数组 | 一维转移方程 |
---|---|---|---|---|---|---|
求至多容量j的最大价值 | dp = [[0]*(capacity+1) for _ in range(n+1)] | dp[i+1][j] = max(dp[i][j], dp[i][j-w[i]]+v[i]) if j>=w[i] else dp[i][j] | dp[n][capacity] | 外层循环所有的物品 i,内层循环遍历所有的容量 j | f = [0] * (capacity+1) | f[j] = max(f[j], f[j-w[i]]+v[i]) |
求恰好为容量j的最大价值 | dp = [[-inf]*(capacity+1) for _ in range(n+1)];dp[0][0]=0 | dp[i+1][j] = max(dp[i][j], dp[i][j-w[i]]+v[i]) if j>=w[i] 【【微信】】[i][j] | dp[n][capacity] | 初始值 dp[0][0] = 0, dp[0][j] = 负无穷 (j != 0) | f = [0] + [-inf] * capacity | f[j] = max(f[j], f[j-w[i]]+v[i]) |
求恰好为容量j的最小价值 | dp = [[inf]*(capacity+1) for _ in range(n+1)];dp[0][0]=0 | dp[i+1][j] = min(dp[i][j], dp[i][j-w[i]]+v[i]) if j>=w[i] else dp[i][j] | dp[n][capacity] | 初始值 dp[0][0] = 0, dp[0][j] = 正无穷 (j != 0) | f = [0] + [inf] * capacity | f[j] = min(f[j], f[j-w[i]]+v[i]) |
求至少容量j的最小价值 | dp = [[inf]*(capacity+1) for _ in range(n+1)];dp[0][0]=0 | dp[i+1][j] = min(dp[i][j], dp[i][j-w[i]]+v[i]) if j>=w[i] else dp[i][j] | dp[n][capacity] | - | f = [0] + [inf] * capacity | f[j] = min(f[j], f[j-w[i]]+v[i]) |
求至多容量j的方案个数 | dp = [[1] + [0]*capacity for _ in range(n+1)] | dp[i+1][j] += dp[i][j-w[i]] if j>=w[i] else dp[i+1][j] =dp[i][j] | dp[n][capacity] | 初始值 dp[i][0] = 1 表示每个容量下至少有一种选法即选择不放任何物品 | f = [1] + [1] * capacity | f[j] += f[j-w[i]] |
求恰好为容量j的方案个数 | dp = [[1] + [0]*capacity for _ in range(n+1)] | dp[i+1][j] += dp[i][j-w[i]] if j>=w[i] else dp[i+1][j] =dp[i][j] | dp[n][capacity] | 初始值 dp[i][0] = 1 | f = [1] + [0] * capacity | f[j] += f[j-w[i]] |
求至少容量j的方案个数 | dp = [[1] + [0]*capacity for _ in range(n+1)] | dp[i+1][j] += dp[i][j-w[i]] if j>=w[i] else dp[i+1][j] =dp[i][j] | dp[n][capacity] | - | f = [1] + [0] * capacity | f[j] += f[j-w[i]] |
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
如表格所示,求恰好容量的方案数,故二维初始数组为dp = [[1] + [0]*capacity for _ in range(n+1)],转移方程为dp[i+1][j] += dp[i][j-w[i]] if j>=w[i] else dp[i+1][j] =dp[i][j] dp[n][capacity],因此我们直接定义
一维数组初始化为f = [1] + [0] * capacity,转移方程为f[j] += f[j-w[i]],且定义
给你一个整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 : 例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。 返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
本题看上去与背包问题无关,但实际为0/1背包的变形问题,我们假设数组和为sum,添加+号的数和为p,那么添加-号的和为sum-p,则有p-(sum-p)=target,则2p=target+sum,则题目的要求可以写为选择数组中的数,使其和恰好为(target+sum)/2。
如表格所示,求恰好容量的方案数,故二维初始数组为dp = [[1] + [0]*capacity for _ in range(n+1)],转移方程为dp[i+1][j] += dp[i][j-w[i]] if j>=w[i] else dp[i+1][j] =dp[i][j] dp[n][capacity],因此我们
一维数组初始化为f = [1] + [0] * capacity,转移方程为f[j] += f[j-w[i]],
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 你可以认为每种硬币的数量是无限的。
该题中对于不同的硬币可以重复选择,故为完全背包问题的变形。 如表格所示,求恰好金额的最少硬币数,即恰好容量的最小价值,每个物品价值为1,故二维初始数组为dp = [[inf]*(capacity+1) for _ in range(n+1)];dp[0][0]=0,转移方程为dp[i+1][j] = min(dp[i][j], dp[i][j-w[i]]+v[i]) if j>=w[i] else dp[i][j],本题的所有v[i]都表示物品数为1,故
由表格可知数组初始化为dp = [0] + [inf] * capacity 转移方程为dp[j] = min(dp[j], dp[j-w[i]]+v[i]),本题的所有v[i]都为1,且
给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。1 <= n <= 104 完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
本题也是对于完全背包的变形,与上题类似,不过其中的物品体积w为1到10000所有的平凡数组成的数组,故
给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带符号整数。
本题求和为amount的方案数,可以直接修改练习一中的zero_one_knapsack_Number_of_schemes()为unbounded_knapsack_Number_of_schemes()来解决,且由示例可知,0/1背包改为完全背包仅需
直接修改练习一中的one_dimensional_zero_one_knapsack_Number_of_schemes()为one_dimensional_unbounded_knapsack_Number_of_schemes()来解决,且由示例可知,0/1背包改为完全背包在用一维数组解决时只需要将。
笔记本360°环绕保护,好看又能装,苹果合作品牌INCASE背包评测|背包|苹果|360
笔记本开7.1环绕,笔记本边缘保护作者:科技爱酱
疫情结束后,我在公司的出差任务也变得多了起来,尤其是2-3日的短途出差,只需携带笔记本、手机、充电器、充电宝、薄外套和一些零碎的随身物品,因此一款合适的背包,才能在旅途中轻装上阵,高效完成出差任务。
那么如何选择适合自己的背包呢?综合来说无非有几个要素:好看能装、坚固耐用、背负轻松,如果能带点防水BUFF加持就更好了。基于上述因素,我入手了一款INCASE City系列16英寸都市通勤背包升级款,这款背包用起来怎么样呢?下来就和大家分享一下使用体验,希望能够对选购背包的朋友们有所帮助。
首先提起INCASE这个品牌,相信不少朋友们会有点陌生。其实这INCASE作为第一个Apple Store销售的第三方品牌,获得过不少IF、RedDot等设计大奖,可见苹果对INCASE的设计和做工还是很认可的。对我们广大消费者来说,在颜值方面也没什么可担心的,出门在外,讲究的就是时尚好看,这也是我选择INCASE背包的一个理由。
INCASE都市系列背包升级款(以下简称:INCASE背包)有黑灰双色,相对于普通款,升级款在材质方面升级到了1680D尼龙,更坚韧的同时也设计了磁吸侧兜,大家可以根据预算和使用场景选择不同的款式。
我这款是黑色款。背包尺寸约为46x33x13cm,整体看起来时尚大方,简约有型,没有异味和线头,做工扎实,适合通勤、旅差等多种场合使用。
背包第一层采用了1680D弹道尼龙面料。弹道尼龙相对于普通的尼龙而言,耐磨度要高出数倍,因此主要是应用于军用和高级户外用品领域,应付日常的使用肯定没有压力,如果能够把1680D弹道尼龙磨破,也不是一件容易的事。
1680D弹道尼龙的强悍耐磨耐刮擦效果大家可以看以下动图,用锋利的美工刀在尼龙表面刮擦,虽然会留下划痕但并不会划伤、起毛刺,正常使用的话,我觉得背包用个五六年都没问题,哪怕你不想用了,背包质量依然如初。
INCASE背包在细节方面,还是很用心的。顶部的LOGO做工精细,肩带和连接卡扣也有INCASE的LOGO,激光切割造型规整,看起来很有档次。
INCASE全部City系列背包都采用防泼水材质,因此在下雨的户外能够起到一定的保护作用。辅以CORDURA面料,让背包具有柔软速干、超轻耐久的特性,进一步提升了整体寿命。
作为一款专为商旅出行和移动创意设计的背包,INCASE背包设置有主仓、电脑专用口袋、正面拉链口袋、iPad口袋、置笔口袋、配件口袋、顶部手机袋、文件口袋、2个侧边水杯袋,用户可以分类收纳,大大提升了载物能力。
主拉链采用双向拉链,其他袋子拉链采用单向拉链,橡胶拉手印有INCASE的LOGO,拉合顺滑静音,可以方便的快取东西。
顶部提手也是加厚尼龙材质,边缘缝线进行了加固处理,可以防止提手断裂,细节好评。
主仓的电脑专用袋用于放置笔记本电脑,最大可以放入16寸的笔记本,还可以放入一些薄衣服、iPad及一些杂物,整体空间较为宽敞。
INCASE独有的360°Protection笔记本环绕保护技术,可以在移动使用的时候更好的保护笔记本。从内部图可以看到,笔记本仓周围有加垫的包体保护,柔韧的360°专用缓震框架包裹电脑四周,厚实的加垫绒毛内衬提供额外的笔记本边角和底部保护,哪怕在剧烈的跑动和掉落的环境下,也能保护笔记本安全。个人觉得略显美中不足的是没有固定笔记本的松紧带,要是有一根把笔记本再固定一下就更完美了。
正面的拉链口袋采用了双层设计,隔层既能防止物品摩擦,也能让物品归类更有序,大家可以放置一些日常出行需要的钱包、钢笔、钥匙、充电宝、驾照、耳机等物品。用尺子量了一下,30cm的深度也挺能装的。
顶层的手机袋也采用和电脑仓一样的加垫绒毛材质,可以全方位保护手机,手机袋完全开启后,内部空间也比较大,放几部手机和眼镜等东西还是绰绰有余的。
顶部的提手手感还是不错的,负载不重的话,提着也很有型。
INCASE背包有左右两个侧袋,均采用了磁性吸合设计,可以放置水杯、雨伞等物品,不用的时候自动吸合让整体更有型。
背包背部采用了人体工学设计的分段式背负系统,肩带和背部负重区域均采用了蜂窝透气面料,保证长时间背负时透气缓重。肩带内衬软硬适中,贴合脊椎的同时也保证了背包在背负的时候不会对双肩造成太大的压力,出行更轻松。
略显遗憾的是,INCASE这款背包没有登机箱固定带,大家有需求可以购买其它款式背包哦。
从整体来看,INCASE背包设计出色,立体感强,填满后挺括有型不易塌陷,配合得体的服饰,轻松打造休闲商务风和都市型男风。
给大家放几张实拍图特写,不管是双肩背还是单肩背,抑或提着,都非常有型。
总结:
这款INCASE City系列16英寸都市通勤背包升级款,作为苹果官方合作品牌,在设计和用料上还是值得肯定的。舒适的背负设计不仅适合商旅和移动办公人士,也适合作为都市日常EDC,它能够合理存储不同的物品,耐用防泼水,能装且好看。此外,笔记本360°环绕保护是一大亮点,同时侧边的磁吸水杯袋和顶部的快取口袋,也方便日常使用。?
阅读更多双肩电脑包精彩内容,可前往什么值得买查看