动态规划-打家劫舍和股票买卖

简介: 前言本篇文章我们来学习下动态规划中的两个经典题型,打家劫舍和股票买卖,通过两个题型来巩固动态规划解题思路。

「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战


前言


本篇文章我们来学习下动态规划中的两个经典题型,打家劫舍和股票买卖,通过两个题型来巩固动态规划解题思路。


打家劫舍57.png

打家劫舍其实比较简单


1. 定义DP数组及下标含义


我们要求的是偷窃n间房子的最大价值,求谁设谁,我们定义DP数组,将dp[i]记录为偷窃[1,i]房子的最大价值


当然,作为职业小偷,为了不出发警报,我们只是偷取[0, i]中的某几间房子

const dp = [] // dp[i] 表示偷窃[0,第i间房子]的最大价值
复制代码


2. 递推公式


如果不考虑是否触发警报,那么偷取[0, i]间房子的最大价值肯定是将它们累加即可。但是在考虑警报的情况下,那么对于第i间房子,我们有两种选择


  • 偷取第i间房子的东西,此时就不能盗窃第i-1的房子

要注意第i间房子对应的价值为nums[i - 1]


dp[i] = nums[i - 1] + dp[i - 2]
复制代码

  • 不偷取第i间房子的东西,此时最大价值就等于偷取[0, i - 1]的最大价值


dp[i] = dp[i - 1]
复制代码


我们综合下两种情况可以得到递推公式


dp[i] = Math.max(dp[i - 1], nums[i] + dp[i - 2])
复制代码


3. DP初始化


我们刚刚定义的dp[i]为偷取第i间房子的最大价值,所以相应的dp[0]表示可偷取的房子为0,而dp[1]等于nums[0]


dp[0] = 0
dp[1] = nums[0]
复制代码


4. dp遍历生成


这步就很简单了,一次循环即可完成


for (let i = 2; i <= m; i++) {
  // ...
}
复制代码


5. 完整代码

var rob = function(nums) {
  const dp = []
  const m = nums.length
  dp[0] = 0
  dp[1] = nums[0]
  for (let i = 2; i <= m; i++) {
    dp[i] = Math.max(dp[i - 1], nums[i - 1] + dp[i - 2])
  }
  return dp[m]
};
复制代码


买卖股票的最佳时机

leetcode-122

58.png

1. 定义DP数组及下标

我们要求的是N天股票买卖最大获利,求谁设谁,我们将dp定义为前i天股票买卖的最大获利,dp[i]表示买卖股票在[0,i]天时的最大获利

const dp = [] // dp[i]表示在[0,第i天]买卖的最大获利
复制代码


2. 递归公式


我们再思考下,什么情况下我们希望吃后悔药,推迟卖出,当然是明天价格比今天高的时候。


什么时候我们会想着立即卖出,或者不买,那应该就是明天价格比今天低的时候。


所以本题的关键在于捕捉到这种价格变化下的理想操作变化。当价格即将下跌之前卖出,在价格上涨前买入。此时我们获利为这种价格的高低差。


  • 当股票价格大于前一天时候,当天应该是卖出的一个时机(当然不代表我们一定会在当天卖出,还得看后面的价格波动),我们的最大获利为上一天为止的最大获利加上
  • 当天与上天的价差

注意第i天的股票价值为i-1

dp[i] = dp[i - 1] + prices[i - 1] - prices[i - 2]
复制代码



  • 当股票价格小于前一天的时候,当天应该是买进的一个时机(不代表我们会在当天买进),我们最大获利和上一天的最大获利相等

dp[i] = dp[i - 1]
复制代码


所以综合两种情况,我们的最大获利递推公式为


if (prices[i - 1] > prices[i - 2]) {
  dp[i] = dp[i - 1] + (prices[i - 1] - prices[i - 2])
} else {
  dp[i] = dp[i - 1]
}
复制代码


3. dp初始化


在第0天和第1天我们都是无法获利的,所以


dp[0] = 0
dp[1] = 0
复制代码


4. dp数组遍历生成


比较简单,我们从第2天开始,一次循环可以完成


for (let i = 2; i <= m; i++) {
  // ...
}
复制代码


5. 完整代码

var maxProfit = function(prices) {
  const dp = []
  const m = prices.length
  dp[0] = 0
  dp[1] = 0
  for (let i = 2; i <= m; i++) {
    if (prices[i - 1] > prices[i - 2]) {
      dp[i] = dp[i - 1] + (prices[i - 1] - prices[i - 2])
    } else {
      dp[i] = dp[i - 1]
    }
  }
  return dp[m]
}
复制代码


结语


本篇文章讲解了leetcode上两个比较经典的系列:打家劫舍和买卖股票,选取了比较有代表性的一题来分析解答过程,通过两道真题让我们巩固动态规划的解题思路。


至此,我将目前学习到的比较经典的动态规划基本已经记录完毕。动态规划实际的难点在于dp数组的定义和递推公式的推导,这也许是通过几道题无法熟练掌握的,可能需要不断低反复练习才能更加理解和掌握。



相关文章
|
人工智能 数据可视化 数据中心
2025 FinOps 状况报告解读
要任务,取代了工作负载优化。企业对 FinOps 的需求从单纯的技术优化转向更广泛的 IT 成本管理,强调自动化和工具投资。此外,FinOps 正与 ITFM、ITAM 和 TBM 深度融合,但 ESG 整合进展缓慢。AI 投资方面,金融行业更倾向于私有云和数据中心,而大多数企业则优先选择公有云。总体而言,FinOps 已成为企业 IT 成本管理的核心,未来将更加注重成本透明化和业务价值量化。
419 11
|
移动开发 JavaScript 小程序
uView Upload 上传
uView Upload 上传
360 0
|
Linux Shell 网络安全
Kickstart 自动化安装
Kickstart结合PXE技术实现Linux系统的自动化安装,适用于需批量部署一致版本的服务器场景,以减少重复劳动。通过搭建Kickstart+DHCP+NFS+TFTP+PXE架构,服务器可远程启动并下载安装配置。具体包括:配置TFTP服务以传输启动文件,设置PXE引导参数指向Kickstart脚本,利用DHCP分配IP地址。这种方式极大地提高了部署效率与一致性。
402 2
|
缓存 Java 数据库
SpringBoot中ThreadPoolTaskExecutor的使用
SpringBoot中ThreadPoolTaskExecutor的使用
648 0
|
机器学习/深度学习 人工智能 自然语言处理
利用深度学习提升语音识别准确率的技术探讨
传统的语音识别技术在面对复杂的语音场景时常常表现出准确率不高的问题。本文探讨了如何利用深度学习技术,特别是深度神经网络,来提升语音识别的精度。通过分析深度学习在语音处理中的应用以及优势,我们展示了如何结合最新的研究成果和算法来解决现有技术的局限性,进一步推动语音识别技术的发展。 【7月更文挑战第3天】
1060 0
|
网络协议 Java 程序员
java版gRPC实战之一:用proto生成代码
《java版gRPC实战》系列的开篇,掌握如何用proto文件生成java代码
1446 1
java版gRPC实战之一:用proto生成代码
|
敏捷开发 存储 安全
敏捷测试四象限
敏捷测试四象限
759 0
敏捷测试四象限
|
弹性计算 云计算
阿里云服务器ECS是什么?ECS英文全称?
阿里云ECS英文全程Elastic Compute Service,弹性计算服务的意思,ECS是阿里云服务器的英文名,一台ECS实例就是一台阿里云服务器
1925 0
阿里云服务器ECS是什么?ECS英文全称?
|
小程序 测试技术 数据安全/隐私保护
uni-app:小程序发布检查步骤及注意事项
本篇最好放到项目的【README.md】文件中,方便每次发布的时候检查纠错,毕竟好记性不如烂笔头。而且其他开发者帮忙修改bug、发布新版本的时候,只需要根据这个事项就能实现整个流程的提审发布,提高效率。
612 0
uni-app:小程序发布检查步骤及注意事项

热门文章

最新文章