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

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

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

动态规划五部曲

1.确定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] 延续买入股票的这个状态

2.确定递推公式

达到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]);

3.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;

4.确定遍历顺序

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

5.举例推导dp数组

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

红色框为最后两次卖出的状态。

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

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

代码

class Solution {public int maxProfit(int[] prices) {int len = prices.length;// 边界判断, 题目中 length >= 1, 所以可省去if (prices.length == 0) return 0;/** 定义 5 种状态:* 0: 没有操作, 1: 第一次买入, 2: 第一次卖出, 3: 第二次买入, 4: 第二次卖出*/int[][] dp = new int[len][5];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];}
}

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

动态规划五部曲

1.确定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 就可以了。

2.确定递推公式

还要强调一下: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])

3.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]

4.确定遍历顺序

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

5 .举例推导dp数组

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

代码

class Solution {public int maxProfit(int k, int[] prices) {if (prices.length == 0) return 0;// [天数][股票状态]// 股票状态: 奇数表示第 k 次交易持有/买入, 偶数表示第 k 次交易不持有/卖出, 0 表示没有操作int len = prices.length;int[][] dp = new int[len][k*2 + 1];// dp数组的初始化, 与版本一同理for (int i = 1; i < k*2; i += 2) {dp[0][i] = -prices[0];}for (int i = 1; i < len; i++) {for (int j = 0; j < k*2 ; j += 2) {dp[i][j + 1] = Math.max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);dp[i][j + 2] = Math.max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);}}return dp[len - 1][k*2];}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker 国内镜像源更换

实现 替换docker 镜像源 前提要求 安装 docker docker-compose 参考创建一键更换docker国内源 vim /docker_daemon.sh #!/bin/bash # -*- coding: utf-8 -*- # Author: make.han # Email: CIASM@CIASM # Date: 2024/06/07 # docker daemon.jsondaemon_json_file="/et…

[网络基础]——计算机网络(OSI)参考模型 详解

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f310;网络通信基础TCP/IP专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年6月2日21点59分 &#x1f004;️文章质量&#xff1a;93分 目录 &#x1f39f;️OSI基本概念 &#x1f384;分层架构…

使用Qt实现文本文件的读写操作

文章目录 文本读写简介QFileDialog简介常用方法示例代码 QFile简介常用方法示例代码 QTextStream简介常用方法示例代码 结合使用示例完整示例代码(读写操作&#xff0c;可直接复制运行我使用的Qt版本为QT5.14)mainwindow.hmainwindow.cppmain.cpp代码解释 文本读写简介 在现代…

前端经典面试手写题

前端面试中常会遇到的手写题主要考察应聘者对前端基础知识的掌握程度以及编程能力。以下是一些经典的前端手写题及其解答思路&#xff1a; 手写深拷贝&#xff08;Deep Clone&#xff09; 深拷贝是指复制一个对象及其所有子对象&#xff0c;使得新的对象和原对象完全独立。 …

聊聊DoIP吧(二)-报文结构和时间参数

书接上回&#xff0c;DoIP报文及其在以太网帧中的位置&#xff1a;图片来自Vector官网 这里我们来看看DoIP报文结构&#xff1a; DoIP协议时间参数详解 - 知乎 (zhihu.com)

实验三-8086指令的应用《计算机组成原理》

一、实验目的 掌握8086指令的应用 二、实验原理 三、实验仪器 计算机1台&#xff0c;emu8086软件。 四、实验步骤 1、建立00H&#xff5e;0FH&#xff5e;00H 31个数&#xff0c;00H&#xff5e;0FH数据逐渐增大,0FH&#xff5e;00H逐渐减小&#xff0c;即DI指针所表示的地…

linux进程间通讯指南-打通IPC大门,高效沟通无阻

在现代操作系统中&#xff0c;进程就像独立的个体&#xff0c;有时需要相互合作、数据共享&#xff0c;这就要求进程间能够高效通信。本文将为你揭开Linux进程间通信(IPC)的神秘面纱&#xff0c;探讨各种IPC工具的运作原理&#xff0c;同步机制的重要性&#xff0c;以及如何规避…

手动安装Nvidia驱动和CUDA Toolkit

1、打开cuda-toolkit 网站 CUDA Toolkit Archive | NVIDIA Developer 根据自己需要选择CUDA Toolkit版本&#xff0c;这里选择12.0.0 2、点击链接跳转到下载页面&#xff0c;选择操作系统类型和安装包类型 3、下载CUDA Toolkit 安装包 4、执行下载命令 wget https://develo…

【C语言】学生管理系统:完整模拟与实现

&#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;C笔记专栏&#xff1a; C笔记 &#x1f308;喜欢的诗句:无人扶我青云志 我自踏雪至山巅 &#x1f525;引言 本篇文章为修改了在校期间实训报告&#xff0c;使用C…

停止一个正在运行的线程

暴力停止方法 stop 该方法是不安全的&#xff0c;已经过时的方法&#xff0c;在其方法描述上 This method is inherently unsafe&#xff0c;这个方法实际上是不安全的 package com.alibaba.fescar.core.protocol.test;public class TestThreadStop {public static void main(S…

基于STM32开发的智能水族箱管理系统

目录 引言环境准备智能水族箱管理系统基础代码实现&#xff1a;实现智能水族箱管理系统 4.1 温度传感器数据读取4.2 水泵与加热器控制4.3 水位传感器数据读取4.4 用户界面与显示应用场景&#xff1a;水族箱管理与优化问题解决方案与优化收尾与总结 1. 引言 智能水族箱管理系…

day51 动态规划 121. 买卖股票的最佳时机 122.买卖股票的最佳时机II

121. 买卖股票的最佳时机 动态规划 1.确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i][0] 表示第i天持有股票所得最多现金 &#xff0c;这里可能有同学疑惑&#xff0c;本题中只能买卖一次&#xff0c;持有股票之后哪还有现金呢&#xff1f; 其实一开始现…

我们正迈向万物互联

物联网&#xff08;Internet of Things&#xff0c;IoT&#xff09;是近年来信息技术领域最引人注目的创新之一。它通过将物理世界与数字世界紧密相连&#xff0c;为我们的日常生活、工业生产、城市管理等多个领域带来了前所未有的变革。本文将深入探讨物联网的基本概念、技术原…

Java 18 新功能概述

Java 18 在 2022 年 3 月 22 日正式发布&#xff0c;Java 18 不是一个长期支持版本。 包含多项新特性和改进&#xff0c;如文件系统链接、文本块、表达式求值API、ForkJoinPool优化、Optional新方法等。 亮点还包括预览特性&#xff1a;Record Pattern Matching for Switch和增…

C++ - 查找算法 和 其他 算法

目录 一. 查找算法&#xff1a; 1.顺序查找&#xff1a; 2.二分查找&#xff1a; 二. 其他算法&#xff1a; 1.遍历算法&#xff1a; 2.求和、求平均值等聚合算法。 a.求和算法&#xff1a; b.求平均值算法&#xff1a; 一. 查找算法&#xff1a; 1.顺序查找&#xff1…

Python深度学习基于Tensorflow(15)OCR验证码 文本检测与识别实例

文章目录 文本检测文本识别CTC层生成验证码并制作数据集建立模型模型推理 参考 文本检测 文本检测和目标检测类似&#xff0c;其不同之处在于文本目标具有序列特征&#xff0c;有连续性&#xff0c;可以通过结合 Faster R-CNN 和 LSTM 的方式进行文本检测&#xff0c;如 CTPN …

学习请求接口

axios的方法 方法一 方法二 方式三 方式四 ajax请求 fetch请求 学习一下

告别冗长代码:Java Lambda 表达式如何简化你的编程

在现代软件开发中&#xff0c;高效和简洁的代码变得越来越重要。Java作为一门成熟而广泛使用的编程语言&#xff0c;一直在不断进化&#xff0c;以满足开发者的需求。Java 8的推出标志着一次重要的飞跃&#xff0c;其中最引人注目的特性之一便是Lambda表达式。 Lambda表达式为J…

RK3588推理RetinaFace出现问题

RK3588推理RetinaFace出现问题 在RK3588上测试RockChip提供的RetinaFace模型时&#xff0c;出现下面的问题 $ python RetinaFace_pic.py done --> Init runtime environment I RKNN: [02:27:16.501] RKNN Runtime Information: librknnrt version: 1.5.2 (c6b7b351a2023…

Windows11下Docker使用记录(五)

目录 准备1. WSL安装cuda container toolkit2. win11 Docker Desktop 设置3. WSL创建docker container并连接cuda4. container安装miniconda&#xff08;可选&#xff09; Docker容器可以从底层虚拟化&#xff0c;使我们能够在 不降级 CUDA驱动程序的情况下使用 任何版本的CU…