经典动态规划问题:含手续费的股票买卖【从 O(n) 到 O(1) 的优化解析】
题目理解
我们要在给定的股票价格数组 prices 中进行买卖操作,并尽可能多次交易以获取最大利润。每次交易都需要支付一定的手续费 fee,因此我们必须考虑如何通过合适的交易策略最大化利润。
在本题中,每一天可以选择:
- 不进行任何操作。
 - 买入股票。
 - 卖出股票(前提是已经持有股票)。
 
题目链接:714. 买卖股票的最佳时机含手续费 - 力扣(LeetCode)
动态规划思路
我们可以使用动态规划(DP)来解决这个问题。在动态规划中,我们定义两种状态:
- 持有股票状态(持仓):
hold[i]表示第i天结束时持有股票时的最大利润。 - 不持有股票状态(空仓):
cash[i]表示第i天结束时不持有股票时的最大利润。 
状态转移方程
-  
持有股票的状态:
我们有两种可能:- 我们在第 
i天之前已经持有股票,那么hold[i]就和hold[i-1]相同。 - 我们在第 
i天买入股票,那么需要从cash[i-1](前一天不持有股票的最大利润)中减去prices[i](当天股票价格)。
因此,持有股票状态的转移方程为:
h o l d [ i ] = max  ( h o l d [ i − 1 ] , c a s h [ i − 1 ] − p r i c e s [ i ] ) hold[i] = \max(hold[i-1], cash[i-1] - prices[i]) hold[i]=max(hold[i−1],cash[i−1]−prices[i]) 
 - 我们在第 
 -  
不持有股票的状态:
我们有两种可能:- 我们在第 
i天之前已经卖出了股票,那么cash[i]就和cash[i-1]相同。 - 我们在第 
i天卖出股票,此时需要加上prices[i]的收入并扣除手续费fee。
因此,不持有股票状态的转移方程为:
c a s h [ i ] = max  ( c a s h [ i − 1 ] , h o l d [ i − 1 ] + p r i c e s [ i ] − f e e ) cash[i] = \max(cash[i-1], hold[i-1] + prices[i] - fee) cash[i]=max(cash[i−1],hold[i−1]+prices[i]−fee) 
 - 我们在第 
 
初始状态
hold[0] = -prices[0]:第0天如果买入股票,我们的利润就是负的第0天的股票价格。cash[0] = 0:第0天如果不买股票,利润为0。
最终结果
我们最终需要的是在最后一天结束时,不持有股票时的最大利润,即 cash[n-1],其中 n 是 prices 的长度。
C++ 实现
#include <vector>
#include <algorithm>
using namespace std;int maxProfit(vector<int>& prices, int fee) {int n = prices.size();vector<int> hold(n), cash(n);hold[0] = -prices[0]; // 第 0 天持有股票cash[0] = 0;          // 第 0 天不持有股票for (int i = 1; i < n; ++i) {cash[i] = max(cash[i-1], hold[i-1] + prices[i] - fee); // 不持有股票hold[i] = max(hold[i-1], cash[i-1] - prices[i]);       // 持有股票}return cash[n-1];  // 最后一天不持有股票的最大利润
} 
优化思路
这个基本解法需要两个数组 hold 和 cash,分别存储持有和不持有股票时的利润值。这会占用 O(n) 的空间。而实际上,在计算第 i 天的状态时,只依赖于 i-1 天的状态,因此我们可以使用两个变量代替数组,优化空间复杂度到 O(1)。
优化后的实现
#include <vector>
#include <algorithm>
using namespace std;int maxProfit(vector<int>& prices, int fee) {int n = prices.size();int hold = -prices[0]; // 持有股票时的最大利润int cash = 0;          // 不持有股票时的最大利润for (int i = 1; i < n; ++i) {cash = max(cash, hold + prices[i] - fee); // 更新不持有股票的最大利润hold = max(hold, cash - prices[i]);       // 更新持有股票的最大利润}return cash;  // 最后一天不持有股票的最大利润
}
 
解释及示例
示例 1
输入:prices = [1, 3, 2, 8, 4, 9], fee = 2
输出:8
过程:
- 第 0 天:买入股票,
hold = -1,cash = 0。 - 第 1 天:卖出股票,
cash = max(0, -1 + 3 - 2) = 0,保持持有状态,hold = -1。 - 第 2 天:保持持有状态,
cash = 0,hold = max(-1, 0 - 2) = -1。 - 第 3 天:卖出股票,
cash = max(0, -1 + 8 - 2) = 5,hold = max(-1, 5 - 8) = -1。 - 第 4 天:买入股票,
cash = 5,hold = max(-1, 5 - 4) = 1。 - 第 5 天:卖出股票,
cash = max(5, 1 + 9 - 2) = 8,hold = 1。 
最终结果:cash = 8。
示例 2
输入:prices = [1, 3, 7, 5, 10, 3], fee = 3
 输出:6
- 第 0 天:买入股票,持有股票的利润为 
hold = -1,不持有股票的利润为cash = 0。 - 第 1 天:卖出股票后利润为 
cash = max(0, -1 + 3 - 3) = 0,持有状态hold = max(-1, 0 - 3) = -1。 - 第 2 天:卖出股票后利润为 
cash = max(0, -1 + 7 - 3) = 3,持有状态hold = max(-1, 3 - 7) = -1。 - 第 3 天:保持不持有状态 
cash = max(3, -1 + 5 - 3) = 3,持有状态hold = max(-1, 3 - 5) = -1。 - 第 4 天:卖出股票后利润为 
cash = max(3, -1 + 10 - 3) = 6,持有状态hold = max(-1, 6 - 10) = -1。 - 第 5 天:保持不持有状态 
cash = max(6, -1 + 3 - 3) = 6。 
最终利润为 6。
关键点总结
-  
最优子结构:第
i天的状态只取决于第i-1天的状态。 -  
状态转移方程:
- 持有状态:
hold[i] = max(hold[i-1], cash[i-1] - prices[i]) - 不持有状态:
cash[i] = max(cash[i-1], hold[i-1] + prices[i] - fee) 
 - 持有状态:
 -  
空间优化:我们只需要两个变量
hold和cash,可以将空间复杂度从O(n)优化到O(1)。 
相关文章:
经典动态规划问题:含手续费的股票买卖【从 O(n) 到 O(1) 的优化解析】
题目理解 我们要在给定的股票价格数组 prices 中进行买卖操作,并尽可能多次交易以获取最大利润。每次交易都需要支付一定的手续费 fee,因此我们必须考虑如何通过合适的交易策略最大化利润。 在本题中,每一天可以选择: 不进行任…...
Python画笔案例-088 绘制 滚动的汉字
1、绘制 滚动的汉字 通过 python 的turtle 库绘制 滚动的汉字,如下图: 2、实现代码 绘制 滚动的汉字,以下为实现代码: """滚动的汉字.py """ import time from turtle import * from write_patch import *width,height...
Redis 5.0 安装配置(Windows)
Redis 5.0之后支持Redis Stream等功能 下载地址:Releases tporadowski/redis GitHub 点击运行redis-server.exe 此外:Redis 6.0及以后版本目前都没有Windows版...
金融行业:办公安全防护专属攻略
在中国金融市场快速迈向数字化转型的进程中,数据安全与隐私保护成为了不容忽视的关键议题。面对不断升级的网络威胁和日益严格的监管要求,构建一个既能支持创新又能提供坚实防护的信息安全体系变得尤为重要。亿格云不断深耕办公安全领域,为金…...
python如何基于numpy pandas完成复杂的数据分析操作?
数据分析是现代数据科学的重要组成部分,Python作为一种强大的编程语言,提供了许多库来简化数据分析过程。 其中,NumPy和Pandas是两个最常用的库。NumPy主要用于数值计算,而Pandas则提供了强大的数据结构和数据分析工具。 本文将深入探讨如何利用这两个库进行复杂的数据分…...
Linux中定时任务调度工具——crontab
1.简介 crontab是Unix和类Unix操作系统(如Linux)中用于定时任务调度的工具。其名称来源于“cron”这个守护进程,它负责周期性地执行任务,并且“tab”表示这个工具的配置文件。用户可以通过配置crontab文件来设定定时任务…...
思维+差分,CF 1884C - Medium Design
目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1884C - Medium Design 二、解题报告 1、思路分析 考虑 最大值 和 最小值…...
简单介绍冯诺依曼体系
现代的计算机, 大多遵守冯诺依曼体系结构 CPU中央处理器:进行算术运算和逻辑判断。存储器:分为外存和内存,用于存储数据(使用二进制方式存储)。输入设备:用户给计算机发号施令。输出设备:计算机…...
kernel32.dll下载地址:如何安全地恢复系统文件
关于从网络上寻找kernel32.dll的下载地址,这通常不是一个安全的做法,而且可能涉及到多种风险。kernel32.dll是Windows操作系统的核心组件之一,负责内存管理、进程和线程管理以及其他关键系统功能。因为kernel32.dll是系统的基础文件ÿ…...
【高等数学】多元微分学 (一)
偏导数 偏导数定义 如果二元函数 f f f 在 x 0 , y 0 x_0,y_0 x0,y0 的某邻域有定义, 且下述极限存在 lim  Δ x → 0 f ( x 0 Δ x , y 0 ) − f ( x 0 , y 0 ) Δ x \lim_{\Delta x\to 0} \frac{f(x_0\Delta x,y_0)-f(x_0,y_0)}{\Delta x} Δx→0limΔxf(x0Δ…...
Python爬取京东商品信息,详细讲解,手把手教学(附源码)
Python 爬虫爬取京东商品信息 下面我将逐一解释每一部分的代码 导入库 from selenium import webdriver from selenium.webdriver.edge.service import Service from selenium.webdriver.edge.options import Options import time import random import csv from selenium.c…...
大家有没有了解过TLKS-PLGS这款接地电阻在线监测装置?它在电力系统中能发挥什么作用呢?
接地电阻在线监测仪(输电铁塔接地电阻监测装置、变电站接地电阻监测装置、三极法接地网电阻监测装置)在电力系统中发挥着至关重要的作用,具体来说,有以下几个方面: 一、实时监测预警。该装置采用激励脉冲技术…...
Shell中的函数
目录 一、系统函数 (一)前言 (二)常用的函数 basename [string/pathname] [suffix] 二、自定义函数 (一)语法 (二)脚本例子 三、函数实际案例 过程中的报错: …...
通过IP地址或者主机名添加打印机20241023
文印室打印机连接方式20241023 Win键盘搜索打印机和扫描仪点击添加打印机或扫描仪,等候片刻点击“我需要的打印机不在列表中”添加打印机,选择使用IP地址或主机名添加打印机点击下一步,设备类型选择自动检测输入主机名:即打印机有…...
基于SpringBoot+Vue智慧养老关爱系统【提供源码+答辩PPT+参考文档+项目部署】
💥 这两年毕业设计和毕业答辩的要求和难度不断提升,传统的JavaWeb项目缺少创新和亮点,往往达不到毕业答辩的要求! ❗如何解决这类问题? 让我们能够顺利通过毕业,我也一直在不断思考、努力、精进。通过2024年…...
新手教学系列——利用短效代理快速搭建代理池
引言 在进行高并发数据抓取时,很多人都会遇到频繁IP被封的问题。要解决这个问题,代理池的搭建就成了关键。通过频繁更换代理IP,我们可以绕过网站的反爬机制,提升抓取效率。然而,很多初学者可能会觉得构建一个健壮的代理池颇为复杂,尤其是需要快速切换的短效代理池。在这…...
实体与DTO如何转换
下面是一些常用的转换库: Dozer 该项目目前不活跃,并且很可能在未来被弃用。 ModelMapper 一个智能对象映射库,可自动将对象相互映射。它采用基于约定的方法,同时提供简单、重构安全的应用程序接口(API)来…...
Docker 安装Postgres和PostGIS,并制作镜像
1. 查找postgres和postgis现有的镜像和版本号 镜像搜索网站:https://docker.aityp.com/ 测试使用的是postgres:15.4 和 postgis:15-3.4 2、镜像拉取 docker pull postgres:15.4docker pull postgis/postgis:15-3.4镜像下载完成,docker images 查看如…...
ES6:let和const命令解读以及变量的解构赋值
有时候,我们需要的不是答案,而是一双倾听的耳朵 文章目录 let和const命令变量的解构赋值 let和const命令 let和const命令都是声明变量的关键字,类同varlet特点 用来声明变量,不能再次定义,但是值可以改变存在块级作用…...
java-collection集合整理0.9.4
java-集合整理0.9.0 基本结构基本概念实例化举例遍历获取指定值 2024年10月17日09:43:16–0.9.0 2024年10月18日11:00:59—0.9.4 基本结构 Collection 是最顶级的接口。分为 List 和 Set 两大类。List 分为:ArrayList、LinkedList、Vector。Set 分为:Ha…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
