🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6
🍨 阿珊和她的猫_CSDN个人主页
🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》
🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入门到实战全面掌握 uni-app》
文章目录
- 4. 动态规划的应用
- 背包问题
- 最长公共子序列问题
- 斐波那契数列问题
- 5. 动态规划的优化技巧
- 备忘录法
- 动态规划表格
- 状态压缩
- 6. 总结
- 动态规划的优势和局限
- 动态规划在实际问题中的应用
4. 动态规划的应用
背包问题
背包问题(Knapsack Problem)是动态规划的一个经典应用。
它描述了在给定总容量的情况下,如何选择物品使得总价值最大。
具体来说,背包问题可以描述为:给定一个物品集合,每个物品有一个重量和一个价值,以及一个固定的背包容量。我们的目标是选择一些物品放入背包中,使得背包中的总价值最大,同时不超过背包的容量。
以下是使用动态规划来解决背包问题的步骤:
一、定义状态
在背包问题中,我们可以定义状态为 dp[i][j]
,表示在前 i
个物品中,选择重量不超过 j
的物品的最大价值。
二、确定状态转移方程
状态转移方程是指根据当前状态和决策来更新状态。在背包问题中,状态转移方程为:
dp[i][j] = max(dp[i-1][j], dp[i-1][j - weight[i]] + value[i])
其中,dp[i-1][j]
表示不选择第 i
个物品的最大价值,dp[i-1][j - weight[i]] + value[i]
表示选择第 i
个物品的最大价值。
三、计算最优值
在确定了状态和状态转移方程后,我们可以通过迭代计算每个状态的最优值,最终得到整个问题的最优解。
具体来说,我们可以从第一个物品开始,依次计算每个物品的状态转移方程,更新 dp
数组。最终,dp[n][capacity]
就是在所有物品中选择不超过背包容量的物品的最大价值。
需要注意的是,在实际问题中,可能需要根据问题的具体情况来选择合适的状态和状态转移方程,并且可能需要使用一些优化技巧来提高算法的效率。
最长公共子序列问题
最长公共子序列问题(Longest Common Subsequence,LCS)是动态规划的另一个经典应用。它描述了在两个字符串中找到最长公共子序列的长度。
具体来说,最长公共子序列问题可以描述为:给定两个字符串 s1
和 s2
,找到它们的最长公共子序列的长度。
以下是使用动态规划来解决最长公共子序列问题的步骤:
一、定义状态
在最长公共子序列问题中,我们可以定义状态为 dp[i][j]
,表示在字符串 s1
的前 i
个字符和字符串 s2
的前 j
个字符中,最长公共子序列的长度。
二、确定状态转移方程
状态转移方程是指根据当前状态和决策来更新状态。在最长公共子序列问题中,状态转移方程为:
dp[i][j] = max(dp[i-1][j], dp[i][j-1], dp[i-1][j-1] + 1)
其中,dp[i-1][j]
表示在字符串 s1
的前 i-1
个字符和字符串 s2
的前 j
个字符中,最长公共子序列的长度,dp[i][j-1]
表示在字符串 s1
的前 i
个字符和字符串 s2
的前 j-1
个字符中,最长公共子序列的长度,dp[i-1][j-1] + 1
表示在字符串 s1
的前 i-1
个字符和字符串 s2
的前 j-1
个字符中,它们的最后一个字符相同的情况下,最长公共子序列的长度加 1。
三、计算最优值
在确定了状态和状态转移方程后,我们可以通过迭代计算每个状态的最优值,最终得到整个问题的最优解。
具体来说,我们可以从第一个字符开始,依次计算每个字符的状态转移方程,更新 dp
数组。最终,dp[n][m]
就是两个字符串的最长公共子序列的长度。
需要注意的是,在实际问题中,可能需要根据问题的具体情况来选择合适的状态和状态转移方程,并且可能需要使用一些优化技巧来提高算法的效率。
斐波那契数列问题
斐波那契数列问题(Fibonacci Number Problem)是动态规划的另一个经典应用。
它描述了在给定两个初始值的情况下,如何计算斐波那契数列的后续数值。
具体来说,斐波那契数列问题可以描述为:给定两个整数 a
和 b
,计算斐波那契数列中第 n
个数的值。
以下是使用动态规划来解决斐波那契数列问题的步骤:
一、定义状态
在斐波那契数列问题中,我们可以定义状态为 dp[i]
,表示斐波那契数列中第 i
个数的值。
二、确定状态转移方程
状态转移方程是指根据当前状态和决策来更新状态。在斐波那契数列问题中,状态转移方程为:
dp[i] = dp[i-1] + dp[i-2]
其中,dp[i-1]
表示斐波那契数列中第 i-1
个数的值,dp[i-2]
表示斐波那契数列中第 i-2
个数的值。
三、计算最优值
在确定了状态和状态转移方程后,我们可以通过迭代计算每个状态的最优值,最终得到整个问题的最优解。
具体来说,我们可以从第一个数开始,依次计算每个数的状态转移方程,更新 dp
数组。最终,dp[n]
就是斐波那契数列中第 n
个数的值。
需要注意的是,在实际问题中,可能需要根据问题的具体情况来选择合适的状态和状态转移方程,并且可能需要使用一些优化技巧来提高算法的效率。
5. 动态规划的优化技巧
备忘录法
备忘录法(Memoization
)是一种常用的动态规划优化技巧,它通过存储已经计算过的子问题的解,避免了重复计算,从而提高了算法的效率。
具体来说,备忘录法的基本思想是在计算每个状态的最优值时,先检查该状态是否已经计算过,如果已经计算过,则直接返回存储的结果,否则计算该状态的最优值,并将其存储起来,以便下次计算时使用。
以下是使用备忘录法来解决斐波那契数列问题的步骤:
一、定义状态
在斐波那契数列问题中,我们可以定义状态为 dp[i]
,表示斐波那契数列中第 i
个数的值。
二、确定状态转移方程
状态转移方程是指根据当前状态和决策来更新状态。在斐波那契数列问题中,状态转移方程为:
dp[i] = dp[i-1] + dp[i-2]
其中,dp[i-1]
表示斐波那契数列中第 i-1
个数的值,dp[i-2]
表示斐波那契数列中第 i-2
个数的值。
三、使用备忘录
在使用备忘录法时,我们需要创建一个备忘录数组 memo
,用于存储已经计算过的状态的解。在计算状态 dp[i]
的最优值时,先检查 memo[i]
是否已经计算过,如果已经计算过,则直接返回 memo[i]
,否则计算 dp[i]
的值,并将其存储在 memo[i]
中。
四、计算最优值
在确定了状态和状态转移方程后,我们可以通过迭代计算每个状态的最优值,最终得到整个问题的最优解。
具体来说,我们可以从第一个数开始,依次计算每个数的状态转移方程,更新 dp
数组。在计算状态 dp[i]
的最优值时,先检查 memo[i]
是否已经计算过,如果已经计算过,则直接返回 memo[i]
,否则计算 dp[i]
的值,并将其存储在 memo[i]
中。最终,dp[n]
就是斐波那契数列中第 n
个数的值。
需要注意的是,在实际问题中,可能需要根据问题的具体情况来选择合适的状态和状态转移方程,并且可能需要使用一些优化技巧来提高算法的效率。
动态规划表格
动态规划表格(Dynamic Programming Table)是一种常用的动态规划优化技巧,它通过将状态和状态转移方程表示为表格的形式,来提高算法的效率。
具体来说,动态规划表格是一个二维数组,其中每一行表示一个状态,每一列表示一个决策。在表格中,每个元素表示该状态下执行该决策的最优值。
以下是使用动态规划表格来解决斐波那契数列问题的步骤:
一、定义状态
在斐波那契数列问题中,我们可以定义状态为 dp[i]
,表示斐波那契数列中第 i
个数的值。
二、确定状态转移方程
状态转移方程是指根据当前状态和决策来更新状态。在斐波那契数列问题中,状态转移方程为:
dp[i] = dp[i-1] + dp[i-2]
其中,dp[i-1]
表示斐波那契数列中第 i-1
个数的值,dp[i-2]
表示斐波那契数列中第 i-2
个数的值。
三、创建动态规划表格
在使用动态规划表格时,我们需要创建一个二维数组 dp
,其中每一行表示一个状态,每一列表示一个决策。在斐波那契数列问题中,我们只需要一行,因为只有一个决策。
四、填充动态规划表格
在确定了状态和状态转移方程后,我们可以通过迭代计算每个状态的最优值,最终得到整个问题的最优解。
具体来说,我们可以从第一个数开始,依次计算每个数的状态转移方程,更新 dp
数组。在计算状态 dp[i]
的最优值时,直接从 dp
表格中获取 dp[i-1]
和 dp[i-2]
的值,进行加法运算,并将结果存储在 dp[i]
中。最终,dp[n]
就是斐波那契数列中第 n
个数的值。
需要注意的是,在实际问题中,可能需要根据问题的具体情况来选择合适的状态和状态转移方程,并且可能需要使用一些优化技巧来提高算法的效率。
状态压缩
状态压缩(State Compression)是一种常用的动态规划优化技巧,它通过减少状态的数量,来提高算法的效率。
具体来说,状态压缩是指将多个相关的状态合并为一个状态,从而减少状态的数量。在状态压缩中,我们需要选择合适的状态表示方法,使得状态之间的关系能够被有效地表示和处理。
以下是使用状态压缩来解决斐波那契数列问题的步骤:
一、定义状态
在斐波那契数列问题中,我们可以定义状态为 dp[i]
,表示斐波那契数列中第 i
个数的值。
二、确定状态转移方程
状态转移方程是指根据当前状态和决策来更新状态。在斐波那契数列问题中,状态转移方程为:
dp[i] = dp[i-1] + dp[i-2]
其中,dp[i-1]
表示斐波那契数列中第 i-1
个数的值,dp[i-2]
表示斐波那契数列中第 i-2
个数的值。
三、使用状态压缩
在使用状态压缩时,我们可以将斐波那契数列的前两个数作为初始状态,然后通过迭代计算后续的状态。具体来说,我们可以使用一个数组 dp
来存储状态,其中 dp[0]
和 dp[1]
分别表示斐波那契数列的前两个数。
四、填充状态压缩数组
在确定了状态和状态转移方程后,我们可以通过迭代计算每个状态的最优值,最终得到整个问题的最优解。
具体来说,我们可以从第一个数开始,依次计算每个数的状态转移方程,更新 dp
数组。在计算状态 dp[i]
的最优值时,直接从 dp
数组中获取 dp[i-1]
和 dp[i-2]
的值,进行加法运算,并将结果存储在 dp[i]
中。最终,dp[n]
就是斐波那契数列中第 n
个数的值。
需要注意的是,在实际问题中,可能需要根据问题的具体情况来选择合适的状态表示方法和状态转移方程,并且可能需要使用一些优化技巧来提高算法的效率。
6. 总结
动态规划的优势和局限
动态规划(Dynamic Programming,DP)是一种常用的算法设计技术,它在解决一些复杂问题时具有明显的优势
,但也存在一些局限。
动态规划的优势:
-
高效性:动态规划可以有效地解决一些具有最优子结构的问题,通常可以在多项式时间内得到最优解。
-
简洁性:动态规划的算法设计通常比较简洁,易于理解和实现。
-
可扩展性:动态规划可以很容易地扩展到更复杂的问题,只需要修改状态定义和状态转移方程即可。
动态规划的局限:
-
状态空间爆炸:对于一些问题,
状态空间的大小可能会随着输入规模的增加呈指数级增长
,导致动态规划无法直接应用。 -
无法处理非最优子结构:
动态规划只能解决具有最优子结构的问题
,如果问题不具有最优子结构,动态规划可能无法提供有效的解决方案。 -
数值稳定性:在一些情况下,动态规划的计算可能会出现数值不稳定的情况,导致结果不准确。
-
依赖于问题的特征:
动态规划的有效性很大程度上依赖于问题的特征
,如果问题的特征不满足动态规划的要求,可能需要寻找其他的解决方法。
总的来说,动态规划在处理一些具有最优子结构的问题时具有明显的优势,但在处理一些复杂问题时可能会受到限制。在实际应用中,需要根据具体问题的特点来选择合适的算法设计技术。
动态规划在实际问题中的应用
动态规划(Dynamic Programming,DP)是一种常用的算法设计技术,它在解决许多实际问题中都有广泛的应用,以下是一些常见的例子:
-
斐波那契数列问题:斐波那契数列是一个经典的动态规划问题,
其中每个数都是前两个数的和。动态规划可以高效地计算出斐波那契数列的任意一项
。 -
背包问题:背包问题是一个在资源有限的情况下选择最优物品组合的问题。动态规划可以用来找出在给定背包容量下可获得的最大价值。
-
最长公共子序列问题:
最长公共子序列是两个序列中共同出现的最长子序列
。动态规划可以用来找到两个序列的最长公共子序列。 -
图像压缩:动态规划可以用于
图像压缩,通过将相似的像素块合并为一个代表性的像素块
,从而减少图像的存储空间。 -
最优路径问题:在图论中,
动态规划可以用来找到从一个节点到另一个节点的最短路径或最低成本路径
。 -
资源分配问题:动态规划可以用于资源分配问题,例如在生产计划中,如何在有限的资源下最大化产量。
-
字符串匹配问题:动态规划可以用于字符串匹配问题,例如
KMP
算法,它是一种高效的字符串匹配算法。
这些只是动态规划在实际问题中的一些应用,实际上,动态规划可以应用于许多其他领域,如金融、生物学、计算机科学
等。动态规划的核心思想是将复杂问题分解为更小的子问题,并通过存储已经计算过的子问题的结果来避免重复计算,从而提高算法的效率。