什么是动态规划和贪心算法
当我们面对复杂的问题时,如何点亮思维的火花并找到解决方案呢?动态规划和贪心算法作为两种常见的算法设计思想,给我们提供了一些启示和应用的思路。这些算法不仅在计算机科学领域有广泛的应用,也可以在生活中的决策和问题解决中发挥重要的作用。
而贪心算法则是一种每次选择局部最优解,并希望通过多次局部最优解达到全局最优解的方法。贪心算法通常用于求解问题的近似解,它对于某些问题可以得到最优解,但对于其他问题可能得不到最优解。贪心算法的关键在于每次选择当前看起来最好的选项,并相信这个选择在后面不会被改变。贪心算法简化了问题的复杂性,减少了计算的时间复杂度,但需要注意的是它并不适用于所有问题。
动态规划和贪心算法的启示与应用不仅局限于计算机科学领域,也可以运用到我们的生活中。在面对困难和复杂的决策时,我们可以借鉴动态规划的思想,将问题拆分成更小的子问题,逐步解决并掌握问题的要点。同时,贪心算法的思想也可以帮助我们做出相对较好的选择,尽管不一定能得到最优解,但可以提高效率和效果。
在接下来的文章中,我们将深入探讨动态规划和贪心算法的原理、特点以及实际应用案例。通过详细的分析和讨论,我们将了解如何点亮思维的火花,并将这些算法应用于解决实际问题。让我们一起探索动态规划和贪心算法的魅力,开启思维的新篇章。
动态规划
动态规划的核心思想是将复杂的问题拆分成更小的子问题,并通过保存子问题的解来避免重复计算。它适用于求解最优化问题,例如最长公共子序列、背包问题等。动态规划的关键在于找到子问题与原问题之间的关系,利用子问题的解构建起整个问题的解。通过迭代求解子问题,最终得到原问题的解。动态规划的优势在于它能够减少重复计算,提高计算效率,并且可以处理一些复杂度较高的问题。
-
原理: 动态规划通过将复杂问题拆解为更小的子问题,并保存子问题的解来避免重复计算。通过求解子问题,最终得到原问题的解。
-
特点:
- 最优子结构:原问题的最优解包含子问题的最优解。子问题的最优解可以通过递归或迭代的方式求解。
- 重叠子问题:子问题之间存在重叠,即相同的子问题会被多次求解。为了避免重复计算,动态规划使用表格或数组来存储子问题的解。
- 自底向上求解:动态规划通常从最小的子问题开始,逐步求解更大规模的子问题,直至求解原问题。
- 状态转移方程:动态规划通过定义状态和状态之间的关系,建立状态转移方程,用于计算子问题的解。
- 时间复杂度:动态规划的时间复杂度通常为O(n^2)或O(n^3),其中n表示问题规模。
-
实例:打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下,一夜之内能够偷窃到的最高金额。
代码:
ini
var rob = function(nums) {
// 没有状态转移方程不要动手
const dp = []; // 最优子结构,O(n) 一位数组,缓存状态
dp[0] = 0;
dp[1] = 0;
for (let i = 2; i < nums.length+2;i++) {
dp[i] = Math.max(dp[i-2] + nums[i-2], dp[i-1])
}
return dp[dp.length - 1]; // 最优
}
具体来说,创建了一个名为dp的数组来存储子问题的解,然后通过循环遍历nums数组,并利用状态转移方程dp[i] = Math.max(dp[i-2] + nums[i-2], dp[i-1])来更新dp数组的值。最终返回dp数组中最后一个元素的值,即为最优解。
贪心算法
贪心算法则是一种每次选择局部最优解,并希望通过多次局部最优解达到全局最优解的方法。贪心算法通常用于求解问题的近似解,它对于某些问题可以得到最优解,但对于其他问题可能得不到最优解。贪心算法的关键在于每次选择当前看起来最好的选项,并相信这个选择在后面不会被改变。贪心算法简化了问题的复杂性,减少了计算的时间复杂度,但需要注意的是它并不适用于所有问题。
-
原理: 贪心算法每次选择局部最优解,并希望通过多次局部最优解达到全局最优解。贪心选择的策略通常是基于当前情况下的最佳选择。
-
特点:
- 贪心选择:贪心算法每次都选择当前看起来最好的选项,相信这个选择在后面不会被改变。
- 不回溯:贪心算法做出选择后,不会回溯修改之前的选择。
- 子问题无关:贪心算法不需要求解子问题或保存子问题的解,每一步的选择只与当前状态有关。
- 时间复杂度:贪心算法通常具有线性时间复杂度O(n),其中n表示问题规模。
-
实例:分发饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i
,都有一个胃口值 g[i]
,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j
,都有一个尺寸 s[j]
。如果 s[j] >= g[i]
,我们可以将这个饼干 j
分配给孩子 i
,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
ini
var findContentChildren = function(g, s) {
g = g.sort((a, b) => a - b);
s = s.sort((a, b) => a - b);
let gi = 0
let sj = 0
let res = 0
while(gi < g.length && sj < s.length){
if (s[sj] >= g[gi]) { // 当下
gi++
sj++
res++
}else {
sj++
}
}
return res
};
函数首先对数组 g 和 s 进行排序,然后使用两个指针 gi 和 sj 分别指向 g 和 s 的起始位置。然后通过一个循环,遍历 g 和 s 数组。
在循环中,如果当前的饼干大小 s[sj] 大于等于当前孩子的胃口 g[gi],则将饼干和孩子都移动到下一个位置,并将结果加一。否则,只移动饼干的指针。
最后,返回结果 res,即满足的孩子数量。
这段代码的时间复杂度为 O(nlogn),其中 n 是输入数组的长度。
总结
综上所述,动态规划和贪心算法是两种重要的算法设计思想,它们在解决问题时具有各自的特点和应用场景。 这两种算法的启示和应用不仅局限于计算机科学,而是可以应用于我们的日常生活。当面对困难的决策或复杂的问题时,我们可以借鉴动态规划的思想,将问题分解为更小的子问题,并逐步解决和掌握问题的关键。同时,贪心算法的思想可以帮助我们做出相对较好的选择,提高效率和效果。
通过深入理解动态规划和贪心算法的原理、特点以及实际应用案例,我们可以点亮思维的火花,并将这些算法应用于解决现实生活中的问题。无论是在工作中还是在个人生活中,运用这些算法思想能够提供有力的支持和指导,帮助我们做出明智的决策和解决复杂的问题。
让我们一起探索动态规划和贪心算法的魅力,开启思维的新篇章,点亮我们的思维火花,并将其转化为行动,为我们的生活带来更大的成功和满足感!
如果本篇文章对你有所帮助,还望点个赞支持一下!