代码随想录算法训练营第四十八天丨 动态规划part11

123.买卖股票的最佳时机III

思路

这道题目相对 121.买卖股票的最佳时机 (opens new window)和 122.买卖股票的最佳时机II (opens new window)难了不少。

关键在于至多买卖两次,这意味着可以买卖一次,可以买卖两次,也可以不买卖。

接来下我用动态规划五部曲详细分析一下:

  • 确定dp数组以及下标的含义

一天一共就有五个状态,

  1. 没有操作 (其实我们也可以不设置这个状态)
  2. 第一次持有股票
  3. 第一次不持有股票
  4. 第二次持有股票
  5. 第二次不持有股票

dp[i][j]中 i表示第i天,j为 [0 - 4] 五个状态,dp[i][j]表示第i天状态j所剩最大现金。

需要注意:dp[i][1],表示的是第i天,买入股票的状态,并不是说一定要第i天买入股票,这是很多人容易陷入的误区

例如 dp[i][1] ,并不是说 第i天一定买入股票,有可能 第 i-1天 就买入了,那么 dp[i][1] 延续买入股票的这个状态。

  • 确定递推公式

达到dp[i][1]状态,有两个具体操作:

  • 操作一:第i天买入股票了,那么dp[i][1] = dp[i-1][0] - prices[i]
  • 操作二:第i天没有操作,而是沿用前一天买入的状态,即:dp[i][1] = dp[i - 1][1]

那么dp[i][1]究竟选 dp[i-1][0] - prices[i],还是dp[i - 1][1]呢?

一定是选最大的,所以 dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1]);

同理dp[i][2]也有两个操作:

  • 操作一:第i天卖出股票了,那么dp[i][2] = dp[i - 1][1] + prices[i]
  • 操作二:第i天没有操作,沿用前一天卖出股票的状态,即:dp[i][2] = dp[i - 1][2]

所以dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2])

同理可推出剩下状态部分:

dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);

dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);

  • dp数组如何初始化

第0天没有操作,这个最容易想到,就是0,即:dp[0][0] = 0;

第0天做第一次买入的操作,dp[0][1] = -prices[0];

第0天做第一次卖出的操作,这个初始值应该是多少呢?

此时还没有买入,怎么就卖出呢? 其实大家可以理解当天买入,当天卖出,所以dp[0][2] = 0;

第0天第二次买入操作,初始值应该是多少呢?应该不少同学疑惑,第一次还没买入呢,怎么初始化第二次买入呢?

第二次买入依赖于第一次卖出的状态,其实相当于第0天第一次买入了,第一次卖出了,然后再买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少。

所以第二次买入操作,初始化为:dp[0][3] = -prices[0];

同理第二次卖出初始化dp[0][4] = 0;

  • 确定遍历顺序

从递归公式其实已经可以看出,一定是从前向后遍历,因为dp[i],依靠dp[i - 1]的数值。

  • 举例推导dp数组

以输入[1,2,3,4,5]为例

123.买卖股票的最佳时机III

大家可以看到红色框为最后两次卖出的状态。

现在最大的时候一定是卖出的状态,而两次卖出的状态现金最大一定是最后一次卖出。如果想不明白的录友也可以这么理解:如果第一次卖出已经是最大值了,那么我们可以在当天立刻买入再立刻卖出。所以dp[4][4]已经包含了dp[4][2]的情况。也就是说第二次卖出手里所剩的钱一定是最多的。

所以最终最大利润是dp[4][4]

以上五部都分析完了,不难写出如下代码:

class Solution {
    public int maxProfit(int[] prices) {
        int len = prices.length;
        if (len==1){
            return 0;
        }
        int[][] dp = new int[len][5];
        /*
         * 定义 5 种状态:
         * 0: 没有操作, 1: 第一次买入, 2: 第一次卖出, 3: 第二次买入, 4: 第二次卖出
         */
        dp[0][1]=-prices[0];
        // 初始化第二次买入的状态是确保 最后结果是最多两次买卖的最大利润
        dp[0][3] = -prices[0];
        for (int i = 1; i < len; i++) {
            dp[i][1] = Math.max(dp[i-1][1],-prices[i]);
            dp[i][2] = Math.max(dp[i-1][2],dp[i-1][1]+prices[i]);
            dp[i][3] = Math.max(dp[i-1][3],dp[i-1][2]-prices[i]);
            dp[i][4] = Math.max(dp[i-1][4],dp[i-1][3]+prices[i]);
        }
        return dp[len-1][4];
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n × 5)

188.买卖股票的最佳时机IV

思路

这道题目可以说是动态规划:123.买卖股票的最佳时机III (opens new window)的进阶版,这里要求至多有k次交易。

动规五部曲,分析如下:

  • 确定dp数组以及下标的含义

在动态规划:123.买卖股票的最佳时机III (opens new window)中,我是定义了一个二维dp数组,本题其实依然可以用一个二维dp数组。

使用二维数组 dp[i][j] :第i天的状态为j,所剩下的最大现金是dp[i][j]

j的状态表示为:

  • 0 表示不操作
  • 1 第一次买入
  • 2 第一次卖出
  • 3 第二次买入
  • 4 第二次卖出
  • .....

大家应该发现规律了吧 ,除了0以外,偶数就是卖出,奇数就是买入

题目要求是至多有K笔交易,那么j的范围就定义为 2 * k + 1 就可以了。

所以二维dp数组的C++定义为:

int len = prices.length;
int[][] dp = new int[len][1 + 2 * k];
  • 确定递推公式

还要强调一下:dp[i][1],表示的是第i天,买入股票的状态,并不是说一定要第i天买入股票,这是很多同学容易陷入的误区

达到dp[i][1]状态,有两个具体操作:

  • 操作一:第i天买入股票了,那么dp[i][1] = dp[i - 1][0] - prices[i]
  • 操作二:第i天没有操作,而是沿用前一天买入的状态,即:dp[i][1] = dp[i - 1][1]

选最大的,所以 dp[i][1] = max(dp[i - 1][0] - prices[i], dp[i - 1][1]);

同理dp[i][2]也有两个操作:

  • 操作一:第i天卖出股票了,那么dp[i][2] = dp[i - 1][1] + prices[i]
  • 操作二:第i天没有操作,沿用前一天卖出股票的状态,即:dp[i][2] = dp[i - 1][2]

所以dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2])

同理可以类比剩下的状态,代码如下:

for (int j = 1; j < 2 * k + 1; j++) {
    if (j % 2 != 0) {
        dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i]);
    } else {
        //j%2==0
        dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + prices[i]);
    }
}

本题和动态规划:123.买卖股票的最佳时机III (opens new window)最大的区别就是这里要类比j为奇数是买,偶数是卖的状态

  • dp数组如何初始化

第0天没有操作,这个最容易想到,就是0,即:dp[0][0] = 0;

第0天做第一次买入的操作,dp[0][1] = -prices[0];

第0天做第一次卖出的操作,这个初始值应该是多少呢?

此时还没有买入,怎么就卖出呢? 其实大家可以理解当天买入,当天卖出,所以dp[0][2] = 0;

第0天第二次买入操作,初始值应该是多少呢?应该不少同学疑惑,第一次还没买入呢,怎么初始化第二次买入呢?

第二次买入依赖于第一次卖出的状态,其实相当于第0天第一次买入了,第一次卖出了,然后在买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少。

所以第二次买入操作,初始化为:dp[0][3] = -prices[0];

第二次卖出初始化dp[0][4] = 0;

所以同理可以推出dp[0][j]当j为奇数的时候都初始化为 -prices[0]

代码如下:

for (int j = 1; j < 2 * k; j += 2) {
    dp[0][j] = -prices[0];
}

在初始化的地方同样要类比j为偶数是卖、奇数是买的状态

  • 确定遍历顺序

从递归公式其实已经可以看出,一定是从前向后遍历,因为dp[i],依靠dp[i - 1]的数值。

  • 举例推导dp数组

以输入[1,2,3,4,5],k=2为例。

188.买卖股票的最佳时机IV

最后一次卖出,一定是利润最大的,dp[prices.size() - 1][2 * k]即红色部分就是最后求解。

以上分析完毕,代码如下:

class Solution {
    public int maxProfit(int k, int[] prices) {
        int len = prices.length;
        int[][] dp = new int[len][1 + 2 * k];
        dp[0][1] = prices[0];
        // dp数组的初始化, 与版本一同理
        for (int i = 1; i < k * 2; i += 2) {
            dp[0][i] = -prices[0];
        }
        for (int i = 1; i < prices.length; i++) {
            for (int j = 1; j < 2 * k + 1; j++) {
                if (j % 2 != 0) {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i]);
                } else {
                    //j%2==0
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + prices[i]);
                }
            }
        }
        return dp[len - 1][2 * k];
    }
}
  • 时间复杂度: O(n * k),其中 n 为 prices 的长度
  • 空间复杂度: O(n * k)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/134842.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

(SpringBoot)第五章:SpringBoot创建和使用

文章目录 一&#xff1a;Spring和SpringBoot&#xff08;1&#xff09;Spring已解决和未解决的问题&#xff08;2&#xff09;SpringBoot 二&#xff1a;Spring项目的创建&#xff08;1&#xff09;IDEA创建&#xff08;2&#xff09;网页端创建 三&#xff1a;项目目录介绍及运…

Mac媒体播放器 Fig Player 1.3.7

Fig Player是一款功能强大的多媒体播放器软件&#xff0c;适用于macOS操作系统。它支持广泛的音频和视频格式&#xff0c;如MP3、MP4、AVI、MKV等&#xff0c;并提供了直观易用的用户界面&#xff0c;使用户可以轻松浏览和管理媒体库。 Fig Player具有快速加载和播放媒体文件的…

人机交互——自然语言生成

自然语言生成是让计算机自动或半自动地生成自然语言的文本。这个领域涉及到自然语言处理、语言学、计算机科学等多个领域的知识。 1.简介 自然语言生成系统可以分为基于规则的方法和基于统计的方法两大类。基于规则的方法主要依靠专家知识库和语言学规则来生成文本&#xff0…

用excel计算矩阵的乘积

例如&#xff0c;我们要计算两个矩阵的乘积&#xff0c; 第一个矩阵是2*2的&#xff1a; 1234 第2个矩阵是2*3的&#xff1a; 5697810 在excel中鼠标点到其它空白的地方&#xff0c;用来存放矩阵相乘的结果&#xff1a; 选择插入-》函数&#xff1a; 选中MMULT&#xff0c;…

PHP生成pdf格式准考证带照片完整示范

PDF效果图 PHP生成pdf格式准考证带照片完整示范以某省公务员考试下载的准考证模板为模板参考&#xff0c;故很有参考意义。 环境支持:linux PHP(5.5-7.3)环境,推荐宝塔环境。 基于fpdf.php插件开发&#xff0c;现有模板适合准考证生成并用于查询下载。 现有排版简单:替换data文…

CenterOS 安装 Jira 需求/BUG管理工具

一、Jira 安装配置 1.1 安装 Jira 下载安装包 https://product-downloads.atlassian.com/software/jira/downloads/atlassian-jira-software-9.5.0-x64.bin将下载的安装包上传至服务器中。 创建 jira 安装目录和数据存放目录 mkdir -p /opt/jira/data添加可运行权限 chmo…

RT-Thread构建与配置系统

简述 RT-Thread的构建与配置系统由以下几个部分组成&#xff1a; KConfig&#xff1a;kernel config配置文件&#xff08;提供系统的配置裁剪功能&#xff09;SCons&#xff1a;构建工具env工具&#xff1a;主要提供构建系统所需的各种环境变量以及软件包的管理 Kconfig在R…

【狂神说Java】Mybatis-plus

✅作者简介&#xff1a;CSDN内容合伙人、信息安全专业在校大学生&#x1f3c6; &#x1f525;系列专栏 &#xff1a;【狂神说Java】 &#x1f4c3;新人博主 &#xff1a;欢迎点赞收藏关注&#xff0c;会回访&#xff01; &#x1f4ac;舞台再大&#xff0c;你不上台&#xff0c…

ESP32网络开发实例-将数据保存到InfluxDB时序数据库

将数据保存到InfluxDB时序数据库 文章目录 将数据保存到InfluxDB时序数据库1、InfluxDB介绍与安装3、软件准备4、硬件准备5、代码实现6、InfluxDB数据可视化在本文中,将介绍 InfluxDB 以及如何将其与 ESP32 开发板一起使用。 我们将向展示如何创建数据库桶并将 ESP32 数据发送…

Python读取csv文件并绘制曲线

前言 有时候我们的数据保存在csv文件中&#xff0c;但是想要更加直观的看出数据的好坏&#xff0c;最好利用matplotlib来画出曲线图 数据准备 我的数据格式如下&#xff1a; 在画图时&#xff0c;我需要把第一行去掉 # 去除第一个元素 xdata xdata.drop(xdata.index[0])…

【Dubbo】Dubbo注册中心原理

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化&#xff0c;文章内容兼具广度、深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于…

工作量证明是解决拜占庭将军问题的唯一办法

号外&#xff1a;教链内参11.10《以太坊反攻》 文 | Oleg Andreev. 2014.5.23. 原标题&#xff1a;Proof That Proof-of-Work is the Only Solution to the Byzantine Generals Problem &#xff08;本文是&#xff09;回复Vlad_Roberto的话&#xff1a;“不&#xff0c;&#…

【SoC基础】DMA的工作原理

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

PTA_乙级_1086

#include <iostream> #include<string> #include<algorithm> using namespace std;int reverseNum(int n){//将数字n转为字符串string nStrto_string(n);//翻转字符串reverse(nStr.begin(),nStr.end());//转为数字int resstoi(nStr);return res; }int main()…

Python 潮流周刊#26:requests3 的现状

△点击上方“Python猫”关注 &#xff0c;回复“1”领取电子书 你好&#xff0c;我是猫哥。这里每周分享优质的 Python、AI 及通用技术内容&#xff0c;大部分为英文。本周刊开源&#xff0c;欢迎投稿[1]。另有电报频道[2]作为副刊&#xff0c;补充发布更加丰富的资讯。 &#…

【LeetCode刷题-二分查找】--69.x的平方根

69.x的平方根 class Solution {public int mySqrt(int x) {int l 0,r x ,ans -1;while(l < r){int mid (r - l)/2 l;if((long)mid * mid <x){ans mid;l mid 1;}else{r mid - 1;}}return ans;} }

云原生微服务架构及实现技术

云原生是一种技术理念和架构方法&#xff0c;它充分利用云计算的优势&#xff0c;将应用程序和基础设施进行优化&#xff0c;以适应云环境的特性。云原生的设计原则主要包括弹性、韧性、安全性、可观测性、灰度等&#xff0c;旨在让企业在云环境中实现轻量、敏捷、高度自动化的…

webpack提升构建速度

目录 配置优化减少 resolve 的解析把 loader 应用的文件范围缩小减少 plugin 的消耗选择合适的 devtool 使用工具thread-loaderDLLPlugin 流程优化拆分构建步骤拆分项目代码 版本更新总结 前端项目随着时间推移和业务发展&#xff0c;页面可能会越来越多&#xff0c;或者功能和…

简单描述下微信小程序的相关文件类型以及微信小程序和uniapp的区别?

前言 微信小程序是一种基于微信平台的轻量级应用&#xff0c;其文件类型涵盖了不同用途的文件。了解这些文件类型能帮助开发者更好地构建小程序。 微信小程序是微信平台上的一种轻量级应用程序&#xff0c;为用户提供了一种便捷的方式来在微信生态系统内进行各种操作和获取服务…

MYSQL索引+常见的锁

一)联合索引: 1)定义:是给一张表上面的多个列增加索引&#xff0c;也就是说给表上面的多个列增加索引&#xff0c;供快速查询使用&#xff0c;当两个列的组合是唯一值时&#xff0c;联合索引是个不错的选择 联合索引和单个索引对比来讲&#xff0c;联合索引的所有索引项都会出现…