面试背包吗,面试高频算法题

PART1 楔子由于01背包的问题属于经典问题。很多文章会直接将经典的解题思路写出来。但是这样,往往会让人容易忘记。让人记住一件事的最好的方法,就是让他了解这件

6ad4c27c79c543c0a65ce3b9e24596cf~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1717695836&x-signature=TjfSt0yk1K0wEfyX%2BVqb6IFwIqU%3D

PART1 楔子

01 因为背包问题是一个经典问题。很多文章直接写出了经典的解题思路。然而,这常常使人忘记。让某人记住某件事的最好方法是确保他们从里到外理解它。在这篇文章中,我们将从最简单的暴力枚举、递归等方法开始,一步步分析各个方法的优化点。文末提供了详细代码地址。我希望这对你有帮助。

PART2 问题描述

现在我们有一个背包(容器),体积(容量)为V。现在有N 种物品(每种物品只有一种)。每一项的值为W[i],占用的空间为W[i]。输入给出的值为C[i]。该背包最多可携带的物品总价值是多少?

首先,我们来快速分析一下这个问题。每个项目只有一个。换句话说,对于任何项目,您要么选择它,要么不选择它。

正如文章开头提到的,首先使用最简单的暴力枚举方案来分析问题,找到方案的缺点,然后继续分析更好的方案。

PART3 朴素的暴力算法

是否选择第一项可以通过允许下次处理第i项时处理完处理后的前两项来轻松解决。通过这样按住,您可以找到处理前n 项后得到的最大总值。

递归算法

分析:

利用递归的思想,如果我们现在从最后一个物体中选择,并且这个物体的重量大于背包的重量,我们可以从第n-1个物体开始选择。

如果这个东西的重量小于你的背包,你将面临两种选择:

1. 如果没有选择,可以跳过此项,从n-1中选择。

2. 选择后,背包的重量将会减少,您可以保存该值并继续选择下一个背包。

最后,我们比较n、n-1、 到0 的值。给定一个比较大小的函数,它会打印最大值。

d31a3d84aef842388695b617cd763268~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1717695836&x-signature=fW7ZbnwrY%2FmK3vmikEHaYcU2vss%3D(PS:详细代码请见文末)

75c41cc7116845a286abcf0c0600e590~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1717695836&x-signature=u5D97BMo6tYnezQnVOfggUXQa2c%3D(PS:详细代码请见文末)

暴力枚举

思路分析:

首先,我们根据项目数量生成所有子集组合。 01 矩阵。这是一个二维数组。接下来进行矩阵遍历判断。判断每层的容量是否超过背包的容量,并确定最大值。还有一种使用格雷码生成子集组合的方法。我这里暂时不做详细介绍。如果您的朋友有兴趣,请自行研究。

b6e9d4b53c9f472388e1c7df2075bccb~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1717695836&x-signature=XrGe2xJWguxtZOmXZKZFHkJQRV8%3D(PS:详细代码请见文末)

PART4 动态规划的原理及过程

1、原理

动态规划将一个大问题划分为更小的问题,通过寻找大问题和小问题之间的递归关系来解决每个小问题,最终达到解决原问题的效果。然而,不同之处在于,分而治之的方法对于子问题、子子问题等等会一遍又一遍地重复。动态规划的记忆方式是填表,记录所有已解决的子问题的答案,并保存不同阶段的不同状态,以便在新问题中使用。可以直接提取,避免重复计算,节省时间。

因此,用动态规划解决问题的核心在于,一旦问题满足最优性原则,填表就会找到最优解。

2. 分析过程

a) 01 背包问题的抽象

(X1,X2,…,Xn,Xi可以为0或1,表示第i项是否被选中)

Vi表示第i个物品的价值,Wi表示第i个物品的体积(重量)。

b) 建立模型

即求max(V1X1+V2X2+…+VnXn)。

c) 限制条件

W1X1+W2X2+…+WnXn 容量

d) 定义V(i,j)。

当前背包容量j,前i个物品的最优组合对应的值。

e) 最优性原理是动态规划的基础

最优性原则指出[多步决策过程中的最优决策序列具有以下属性:

无论初始状态还是第一个决策,对于前一个决策引起的某个状态,

后续阶段的决策顺序必须构成最优策略。 ]

判断问题是否满足最优性原则,并用反证法证明:

假设(X1,X2,…,Xn)是01背包问题的最优解。

在这种情况下,(X2, X3,…,Xn) 是该子问题的最优解。

假设(Y2,Y3,Yn)是上述问题的子问题的最优解。

那么应该有

(V2Y2+V3Y3+.+VnYn)+V1X1(V2X2+V3X3+.+VnXn)+V1X

且(V2X2+V3X3+…+VnXn)+V1X1=(V1X1+V2X2+…+VnXn)

然后我们有(V2Y2+V3Y3+…+VnYn)+V1X1 (V1X1+V2X2+…+VnXn)。

该方程表明(X1,Y2,Y3,Yn)是01背包问题的最优解。

这与第一个假设相矛盾,即(X1, X2,Xn) 是01 背包问题的最优解。

因此,01背包问题满足最优性原则。

f) 求递推公式

当前的产品有两种可能性。

首先,袋子的容量小于产品的体积,因此无法装载。此时的值与第i-1个值相同,即V(i,j)=V。 (i-1,j);

然后,您有足够的空间来存储该产品,但即使安装了也不一定达到当前的最佳状态,因此选择最佳选项:安装或不安装。

V(i,j)=max{V(i-1,j), V(i-1,j-w(i))+v(i)}

其中,V(i-1,j)表示不装载,V(i-1,j-w(i))+v(i)表示装载第i个产品,背包的容量减少w (i),但其值增加v(i)。

由此我们可以推导出递归关系。

1) jw(i) V(i,j)=V(i-1,j)

2) j=w(i) V(i,j)=max{V(i-1,j), V(i-1,j-w(i))+v(i)}

g) 填写表格

首先,初始化边界条件V(0,j)=V(i,0)=0。

fa96377c397444afbbb30b1c8b8139b0~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1717695836&x-signature=3gfp2DxE7f6njeykYQUQJ%2BQ7KMc%3D(注:数量:数量=4,背包容量:容量=8)

496e0b5580bf4972b0b62e0562965ad2~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1717695836&x-signature=Lmi5YlE0IC1i6P%2BtSUSgJZrc7no%3D(注:横轴为背包容量,纵轴为所选产品下标)

h) 填完表格后,最优解为:V(数量,容量)=V(4,8)=10

3. 二维数组动态规划

3adb11f73be34996b16c6af97aa6f8fc~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1717695836&x-signature=nXlSpQnPhWviswLTU7wj3Z85yEo%3D(PS:详细代码请见文末)

4. Backpack使用滚动数组来压缩空间

所谓滚动阵列的目的就是为了优化空间。从上面的解法我们可以看出,在求解过程中,状态转移矩阵使用了N*V数组。当涉及到以前的状态解决方案时,以前存储的状态信息是无用的,可以丢弃,所以我们只需要空间来存储当前状态和以前的状态,所以我们需要做的就是2*V空间。它以循环滚动的方式达到了与N*V相同的目的。效果一样。这是一个非常大的空间优化。

这是代码:您可以在每个内部循环之后打印当前状态解决方案。比较上面使用二维数组的状态转移矩阵的输出,可以看到将输出重定向到文本具有相同的效果。它将帮助您更好地理解。

3001f22468fc44cf846800d8659722f3~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1717695836&x-signature=hcZR3OxSw3U%2FNU7XcA6pXtejGjk%3D(PS:详细代码请见文末)

5.背包空间降维优化——一维数组

1.什么是空间优化以及为什么需要它?

01 Backpack让我们通过优化数组(使用滚动数组的方法)将原来的N*V的空间复杂度降低到V。也就是说,您可以删除与该项目关联的N。

为什么需要空间优化,首先是因为递归消耗的空间比较大,因为它本质上是用空间换时间。其次,算法竞赛一般都有场地限制。你总会被问到一些优化问题。在面试中养成优化的习惯也有好处。

2.为什么这个问题可以降维?

通过观察,我们发现在正常版本的01背包递归中,f[i][.]只与f[i-1][.]相关,并且一职业一职业都可以使用。滚动这种方法称为滚动数组,因为它重用了数组中的空间。滚动并直接覆盖不再使用的数据。

滚动数组的缺点是它们的代价是擦除大量数据。可能不是所有问题都可用,但这里答案是递归的最后一步,所以递归后可以直接打印。无需调用不再是数据的内容。

444539f7546347fab5437b22c41ad0b4~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1717695836&x-signature=KwWe1oahZ6RzrafUR%2BBOvhtyjZw%3D(PS:详细代码请见文末)

PART5 背包恰好装满

有两种类型的初始化:一种只需要最大值,一种需要完整输入。第一种类型的初始化是将值赋给0。第二种类型的初始化是将一个值赋给负无穷大。该数据实际上并不存在。换句话说,这并不是一个不存在的数字。这已经实现,并且没有与该数字相关的可用数据。

PART6 背包输出最优方案

一般来说,背包问题就是寻找最优值。然而,如果你想输出一个产生这个最优值的解,你可以根据状态转移方程推回,从这个状态找到前一个状态,然后按顺序推入。

因此,有两种实现方法,原理是一样的:直接根据状态转移矩阵进行,并使用附加的状态矩阵来记录最优解路径。

PART7 拓展延升

在上面的分析过程中,我们最终采用了动态规划的思想进行分析。那么,你对动态规划了解多少呢?还有哪些与动态规划类似的分析技术,各自有什么特点呢?下面我们将一一解释。 (PS:动态规划算法最复杂,但也是最有效的,所以我们最后讨论)

分而治之算法

分治算法是最容易与动态规划混淆的算法。然而,它们之间存在重要的差异。稍后会详细介绍。

算法思想:将原问题拆分成几个与原问题结构相似的较小的子问题,递归地求解这些子问题,并结合结果来求解原问题的解,得到特征。问题的规模缩小了。在某种程度上解决问题是很容易的。也就是说,该问题具有最优子结构属性。您可以使用问题来组合分解问题的解决方案。每个分解的子问题都是相互独立的。换句话说,子问题和动态规划最大的区别在于它们不包含子问题。分而治之的问题是相互独立的,不涉及共同的子问题。贪心算法

算法思维:通过在算法中的每个决策点做出最佳选择,得出特定问题的最佳解决方案。这种启发式策略并不总是能产生最佳解决方案。步骤:将优化问题转化为这样的问题。也就是说,我们首先做出选择,然后解决剩余的子问题,以证明原问题总是有一个可以通过贪婪选择获得的最优解。贪婪选择的安全性这表明,在做出贪婪选择后,剩余的子问题具有这样的性质。也就是说,通过将问题的最优解与我们所做的贪心选择相结合,我们得到了原问题的最优解。要素:贪婪选择属性、最优子结构属性贪婪选择属性:通过局部(贪婪)选择可以得到全局最优解。动态规划

算法思想:类似于分而治之,通过组合子问题的解来解决整个问题。不同之处在于,动态规划适合分解通常不相互独立的子问题。在这种情况下,使用分而治之的方法会导致某些子问题被多次计算,而动态规划可以通过记录已解决的子问题来避免重复计算。步骤:递归定义最优解值,并根据计算结果构造最优解值元素。最优子结构。子问题:010 -1010 开始学习动态规划算法,直到完成本文。我敏锐地意识到,我不能放弃在学校学到的数学归纳、数学推导、分析思维。除非你再次拿起它,否则这将很难理解。最近,我开始补充数学知识。

关于背包问题,了解01背包的概念很重要。 01背包的想法也适用于完整背包、多充电背包和混合背包等挑战,我们接下来将进行分析。我们要做的就是学会推导和实现状态转移方程,学会优化空间复杂度和降维。

以后我会继续更新关于背包的文章。敬请期待。也欢迎指正。

文中代码地址:附评论区

原创文章,作者:小条,如若转载,请注明出处:https://www.sudun.com/ask/80005.html

(0)
小条's avatar小条
上一篇 2024年5月31日 上午1:43
下一篇 2024年5月31日 上午1:44

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注