leetCode 188.买卖股票的最佳时机 IV 动态规划 + 状态压缩

news2024/11/27 19:44:51

给你一个整数数组 prices 和一个整数 k ,其中 prices[i] 是某支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说,你最多可以买 k 次,卖 k 次。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入:k = 2, prices = [2,4,1]
输出:2
解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。

示例 2:

输入:k = 2, prices = [3,2,6,5,0,3]
输出:7
解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。
     随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。

 >>思路和分析

这道题目是 的进阶版,这里要求至多有k次交易

>>动规五部曲

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

一天 一共有 j 个 状态 ,dp[i][j] 中 i 表示 第 i 天,j 为[0 - 2*k] 个状态,dp[i][j]表示第 i 天状态 j所剩最大现金

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

"持有" : 不代表就是当天"买入"!可能昨天就买入了,今天保持有的状态

  • ① 我们可以发现,除了0以外,偶数就是不持有,奇数就是持有
  • ② 题目要求是至多有k笔交易,那么j的范围就定义为 2*k+1就可以
vector<vector<int>> dp(prices.size(), vector<int>(2 * k + 1, 0));

2.确定递推公式 

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

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

推导思路:

-----------------------------------------------------------
第 i 天 的五种状态
dp[i][0] 不操作

dp[i][1] 第一天持有股票时剩下的最大金钱
dp[i][1]         dp[i-1][1]
                 dp[i-1][0] - prices[i]

dp[i][2] 第一天不持有股票时剩下的最大金钱
dp[i][2]         dp[i-1][2]
                 dp[i-1][1] + prices[i]

dp[i][3] 第二天持有股票时剩下的最大金钱
dp[i][3]         dp[i-1][3]
                 dp[i-1][2] - prices[i]
dp[i][4] 第二天不持有股票时剩下的最大金钱
dp[i][4]         dp[i-1][4]
                 dp[i-1][3] + prices[i]

-----------------------------------------------------------
dp[i][j+1]       dp[i-1][j+1]
                 dp[i-1][j] - prices[i]

dp[i][j+2]       dp[i-1][j+2]
                 dp[i-1][j+1] + prices[i]


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

 3.dp数组初始化

dp[0][0] = 0;
dp[0][1] = -prices[0];
dp[0][2] = 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];
}

4.确定遍历顺序

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

5.举例推导dp数组

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

(2)以输入[3,3,5,0,0,3,1,4],k = 2为例

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

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {

        if (prices.size() == 0) return 0;
        vector<vector<int>> dp(prices.size(), vector<int>(2 * k + 1, 0));
        for (int j = 1; j < 2 * k; j += 2) {
            dp[0][j] = -prices[0];
        }
        for (int i = 1;i < prices.size(); i++) {
            for (int j = 0; j < 2 * k - 1; j += 2) {
                dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
                dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
            }
        }
        return dp[prices.size() - 1][2 * k];
    }
};
  • 时间复杂度: O(n * k),其中 n 为 prices 的长度
  • 空间复杂度: O(n * k)

>>状态压缩

class Solution {
public:
    // 状态压缩
    int maxProfit(int k, vector<int>& prices) {
        if (prices.size() == 0) return 0;
        int len = prices.size();
        vector<int>dp(2 * k + 1,0);
        for(int j = 1;j < 2 * k;j += 2) {
            dp[j] = -prices[0];
        }
        for(int i=1;i<len;i++) {
            for(int j=0;j < 2*k-1;j += 2) {
                dp[j+1] = max(dp[j+1],dp[j] - prices[i]);
                dp[j+2] = max(dp[j+2],dp[j+1] + prices[i]);
            }
        }
        return dp[2*k];
    }
};
  • 时间复杂度: O(n * k),其中 n 为 prices 的长度
  • 空间复杂度: O(k)

参考和推荐文章、视频

动态规划来决定最佳时机,至多可以买卖K次!| LeetCode:188.买卖股票最佳时机4_哔哩哔哩_bilibili

代码随想录 (programmercarl.com) 

来自代码随想录课堂截图:

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

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

相关文章

国庆中秋特辑(五)MySQL如何性能调优?下篇

目录 5.数据库维护6. 数据库调优工具7.数据库架构优化8.代码层面优化9. 硬件层面优化10. 数据库安全 MySQL 性能优化是一项关键的任务&#xff0c;可以提高数据库的运行速度和效率。以下是一些优化方法&#xff0c;包括具体代码和详细优化方案。 接下来详细介绍&#xff0c;共有…

Hudi第二章:集成Spark

系列文章目录 Hudi第一章&#xff1a;编译安装 Hudi第二章&#xff1a;集成Spark 文章目录 系列文章目录前言一、安装Spark1、安装Spark2.安装hive 二、spark-shell1.启动命令2.插入数据3.查询数据1.转换DF2.查询 3.更新4.时间旅行5.增量查询6.指定时间点查询7.删除数据1.获取…

C/C++字符函数和字符串函数详解————长度受限制的字符串函数

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 C语言刷题 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂。 目录 1.前言 2.长度受限制的字符…

arm 汇编基础指令

实现1-100求和 .text .globl _start_start:mov r0, #1 i&#xff0c;i1mov r1, #100 条件变量i<100mov r2, #0 sumLoop: 循环cmp r0,r1 比较r0和r1的大小bhi stop 当r0>r1时&#xff0c;跳到stop标签a…

Ubuntu服务器安全性提升:修改SSH默认端口号

在Ubuntu服务器上&#xff0c;SSH&#xff08;Secure Shell&#xff09;是一种至关重要的远程连接工具。它提供了一种安全的方式来远程连接和管理计算机系统&#xff0c;通过加密通信来确保数据的保密性和完整性。SSH协议广泛用于计算机网络中&#xff0c;用于远程管理、文件传…

什么是 MyBatis?与 Hibernate 的区别

引言 在现代的应用程序开发中&#xff0c;与数据库的交互是至关重要的。为了简化数据库访问&#xff0c;许多开发者选择使用ORM&#xff08;对象-关系映射&#xff09;框架。MyBatis和Hibernate都是流行的ORM框架&#xff0c;它们可以帮助开发者更轻松地将Java对象映射到数据库…

Springboot+vue的球队训练信息管理系统(有报告),Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的球队训练信息管理系统&#xff08;有报告&#xff09;&#xff0c;Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的球队训练信息管理系统&#xff0c;采用M&…

选择排序、冒泡排序、快速排序、归并排序

1、选择排序 设一个数据集有n个元素&#xff0c;选择这n个元素中最小的一个与第一个元素交换位置&#xff0c;再在剩下的n-1个元素中选择最小的一个与第二个元素交换位置&#xff0c;直到在最后两个元素中选择最小的一个放在倒数第二的位置上&#xff0c;简单选择排序是不稳定…

【Arduino ESP32教程入门】Note

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;那个传说中的man的主页 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;题目大解析3 目录 &#x1f449;&#x1f3fb;arduino开发板引脚USB串行总线串行总线和并行总线的优…

Kerberos常见报错汇总

一.kdb5_util: Password mismatch while reading master key from keyboard 1>.错误复现 2>.错误原因分析 在初始化Kerberos数据库时需要输入密码&#xff0c;2次密码输入不一致就会导致该错误。 3>.解决方案 重新执行"kdb5_util -r YINZHENGJIE.COM create -s…

读书笔记--知识图谱基础概念与关键环节解析

知识图谱相当于一张网&#xff0c;是一种大型知识库&#xff0c;一种揭示实体之间关系的语义网络&#xff0c;是事物及其关系的形式化描述&#xff0c;分为通用知识图谱和领域&#xff08;行业&#xff09;知识图谱&#xff0c;如DBpedia&#xff0c;OpenKG&#xff0c;Wikidat…

IP行业查询API:为用户分析提供帮助

引言 在数字化时代&#xff0c;IP地址不仅代表着设备在互联网上的位置&#xff0c;还蕴含着丰富的信息。IP地址所属行业查询API应运而生&#xff0c;为用户分析提供了有力支持。本文将探讨这一工具的应用&#xff0c;以及对用户分析的帮助。 IP行业API的应用 1. 目标市场定位…

凉鞋的 Unity 笔记 102. 场景层次 与 GameObject 的增删改查

102. 场景层次 与 GameObject 的增删改查 在上一篇&#xff0c;我们完成了 Unity 引擎的 Hello world 输出&#xff0c;并且完成了第一个基本循环&#xff1a; 通过这次基本循环的完成&#xff0c;我们获得了一点点的 Unity 使用经验&#xff0c;这非常重要。 有实践经验后再…

C++(string类)

本节目标&#xff1a; 1、为什么要学习string类 2.标准库中的string类 3.vs和g下string结构说明 1.为什么学习string类 1.1 c语言中的字符串 C 语言中&#xff0c;字符串是以 \0 结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c; C 标准库中提供了一些 str系列的…

ROS2 库包设置和使用 Catch2 进行单元测试

说明 本文的目的是了解如何在 ROS2 中创建库&#xff0c;以供其他 ROS2 包使用。除此之外&#xff0c;本文还介绍了如何使用 catch2 框架编写单元测试。本文的第 1 部分将详细介绍如何创建库包。第 2 部分将介绍 ROS2 软件包如何利用创建的库 上篇 ROS2 库包设置和使用 Catch2…

postgresql-管理数据表

postgresql-管理数据表 创建表数据类型字段约束表级约束模式搜索路径 修改表添加字段删除字段添加约束删除约束修改字段默认值修改字段数据类型重命名字段重命名表 删除表 创建表 在 PostgreSQL 中&#xff0c;使用 CREATE TABLE 语句创建一个新表&#xff1a; CREATE TABLE …

二、BurpSuite Scan扫描

1.Scan details 解释&#xff1a;选择只是爬行还是爬行加代码审计 Scan Type&#xff1a;选择爬行或者代码审计URLs to scan&#xff1a;定义要扫描的网址。Burp将从这些网址开始进行爬行&#xff0c;并默认将包括指定网址文件夹下的所有内容。Protocol settings&#xff1a;使…

【Office】超简单,Excel快速完成不规则合并单元格排序

演示效果&#xff1a;将下图已经合并了的单元格按照单位名称排序并将同一个单位的数据合并在了一起。 Step 1&#xff1a;取消合并 选中所有的数据后&#xff0c;点击 “开始”-“合并单元格” &#xff0c;并且取消数据源的合并。 Step 2&#xff1a;填充数据 选中需要填…

宝塔反代openai官方API接口详细教程,502 Bad Gateway问题解决

一、前言 宝塔反代openai官方API接口详细教程&#xff0c;实现国内使用ChatGPT502 Bad Gateway问题解决&#xff0c; 此方法最简单快捷&#xff0c;没有复杂步骤&#xff0c;不容易出错&#xff0c;即最简单&#xff0c;零代码、零部署的方法。 二、实现前提 一台海外VPS服务…

Python Cartopy地图投影【3】

上两期文章见&#xff1a; Python Cartopy地图投影【1】 第一期文章内容纲要&#xff1a; step1: 开始地图投影 step2: GeoAxes 的常用方法 2.1 add_feature&#xff1a;添加海岸线、河流、湖泊等地理特征 2.2 gridlines&#xff1a;添加网格线以及相应标签等 Python Cartopy地…