LeetCode上的各种股票最大收益

对于力扣平台上的股票类型的题目:

  • 121 买卖股票的最佳时机

  • 122 买卖股票的最佳时机 II

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

  • 124 买卖股票的最佳时机 IV

  • 309 最佳买卖股票时机含冷冻期

  • 714 买卖股票的最佳时机含手续费

  • 剑指 Offer 63. 股票的最大利润

关键是要仔细分析题意,将每天结束后可能存在的状态列出来,并考虑当天与前一天的可能存在的状态转移的关系。另外要注意初始化和空间复杂度的优化。

121. 买卖股票的最佳时机

dp数组含义

本题在某一天共可能有两种状态:

我们构造的 dp 数组的规模为 2×n2\times n2×n,在 C++ 中,即为:vector<<vevtor<int>> dp(prices.size(), vector<int> (2, 0));

递推公式

  • 对于第 iii 天持有股票时的收益,即 dp[i][0]dp[i][0]dp[i][0],可能由以下情况推导而来:

    • 第 i−1i-1i−1 天是也是持有股票的,即当日未进行任何操作:dp[i−1][0]dp[i-1][0]dp[i−1][0]

    • 第 i−1i-1i−1 天是未持有股票的,即当日购买了股票,由于我们只能进行一次股票买卖操作,因此在购买股票之前的 dp[i−1][1]dp[i-1][1]dp[i−1][1] 一定是 0,因此应当减去当日股票价格:0−prices[i]0-prices[i]0−prices[i]

    应当取最大值,从而 dp[i][0]=max(dp[i−1][0],−prices[i])dp[i][0]=max(dp[i-1][0],-prices[i])dp[i][0]=max(dp[i−1][0],−prices[i])

  • 对于第 iii 天未持有股票时的收益,即 dp[i][1]dp[i][1]dp[i][1],可能由以下情况推导而来:

    • 第 i−1i-1i−1 天是是持有股票的,即当日将股票卖出,应当 dp[i−1][0]dp[i-1][0]dp[i−1][0] 加上当日股票价格:dp[i−1][0]+prices[i]dp[i-1][0]+prices[i]dp[i−1][0]+prices[i]
    • 第 i−1i-1i−1 天是未持有股票的,即当日无操作:prices[i−1][1]prices[i-1][1]prices[i−1][1]

    应当取最大值,从而 dp[i][1]=max(dp[i−1][1],dp[i−1][0]+prices[i])dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i])dp[i][1]=max(dp[i−1][1],dp[i−1][0]+prices[i])

初始化

dp 数组中所有项需在 dp[0][0]dp[0][0]dp[0][0]、dp[0][1]dp[0][1]dp[0][1] 确定的情况下得到。

  • dp[0][0]dp[0][0]dp[0][0] 表示第一天持有股票,即第一天购入,应为 −prices[0]-prices[0]−prices[0]
  • dp[0][1]dp[0][1]dp[0][1] 表示第一天未持有股票,应为 0

遍历顺序

从前向后

代码

class Solution {public:int maxProfit(vector<int>& prices) {int lens = prices.size();vector<vector<int>> dp(lens, vector<int> (2));dp[0][0] = -prices[0], dp[0][1] = 0;for (int i=1; i<lens; ++i) {dp[i][0] = max(dp[i-1][0], -prices[i]);dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i]);}return dp[lens-1][1];}
};

空间优化

很明显,第 iii 天的情况只依赖于前一天的情况,而与再前面的情况没有关系,因此,我们没有必要维护一整个 dp 数组,而是维护常数变量即可,优化后:

class Solution {public:int maxProfit(vector<int>& prices) {int lens = prices.size();vector<vector<int>> dp(2, vector<int> (2));dp[0][0] = -prices[0], dp[0][1] = 0;for (int i=1; i<lens; ++i) {dp[1][0] = max(dp[0][0], -prices[i]);dp[1][1] = max(dp[0][1], dp[0][0] + prices[i]);dp[0] = dp[1];}return dp[1][1];}
};

另一种做法

class Solution {public:int maxProfit(vector<int>& prices) {int ans = 0, prevMin = INT_MAX;for (int n: prices) {ans = max(ans, n - prevMin);prevMin = min(prevMin, n);}return ans;}
};

122. 买卖股票的最佳时机 II

本题与121 题的唯一区别在于可以多次买卖股票,但要注意同时只能持有一只股票

dp数组含义

递推公式

注意由于这里与上一题的唯一区别在于允许多次股票买卖,因此递推公式与上面的四种情况基本相同,唯一一点在于由于我们可以进行多次买卖,所以在某次购买股票之前的未持有股票的收益不一定是 0 了(我们可能在这次操作之前已经通过几次操作收获了一些利润)。

即对于 dp[i][0]dp[i][0]dp[i][0] 在前一日不持有股票的情况从 0−prices[i]0-prices[i]0−prices[i] 变为了 dp[i−1][1]−prices[i]dp[i-1][1]-prices[i]dp[i−1][1]−prices[i],从而 dp[i][0]=max(dp[i−1][0],dp[i−1][1]−prices[i])dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i])dp[i][0]=max(dp[i−1][0],dp[i−1][1]−prices[i])

初始化遍历顺序与前题类似。

代码

class Solution {public:int maxProfit(vector<int>& prices) {int lens = prices.size();vector<vector<int>> dp(lens, vector<int> (2));dp[0][0] = -prices[0], dp[0][1] = 0;for (int i=1; i<lens; ++i) {dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i]);dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i]);}return dp[lens-1][1];}
};

注意代码中的唯一区别在于:

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

原因上面已经解释过。

空间优化的实现与上面也类似,这里就不贴了。

另一种写法

本题有另一种做法,由于我们可以进行无限次交易且当日即可以买入又可以卖出,所以考虑:

[7, 1, 5, 6] 第二天买入,第四天卖出,收益最大(6-1),所以一般人可能会想,怎么判断不是第三天就卖出了呢? 这里就把问题复杂化了,根据题目的意思,当天卖出以后,当天还可以买入,所以其实可以第三天卖出,第三天买入,第四天又卖出((5-1)+ (6-5) === 6 - 1)。所以算法可以直接简化为只要今天比昨天大,就卖出

class Solution {public:int maxProfit(vector<int>& prices) {int ans = 0;for (int i=1; i<prices.size(); ++i) {int profit = prices[i] - prices[i-1];ans = max(ans, ans + profit);}return ans;}
};

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

本题相较于前面两题要复杂不少,关键是最多两次交易,即有可能可以是 0,1,2 次交易,我们需要列举处理的情况多了不少。

dp数组的定义

本题中,我们一共要考虑每一天的五种状态:

注意我们这里要强调各个状态是 买入/卖出 后 ,即不一定是当日买入/卖出,可能是 i−1i-1i−1,i−2i-2i−2 …日进行的操作,都归为该状态。并且,注意到,这些状态是有序的,即一定是一次经历到的。

由此,我们 dp 数组的含义为:dp[i][j]dp[i][j]dp[i][j] 表示,第 iii 天处于状态 jjj 时的最大收益。

递推公式

  • 对于第 iii 日结束后。处于第一次买入后状态,即 dp[i][1]dp[i][1]dp[i][1],可能由以下情况推导而来:

    • 前面已经买入,当日未进行操作:dp[i−1][1]dp[i-1][1]dp[i−1][1]
    • 当日买入:dp[i−1][0]−prices[i]dp[i-1][0]-prices[i]dp[i−1][0]−prices[i]

    取最大值,则有:dp[i][1]=max(dp[i−1][1],dp[i−1][0]−prices[i])dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i])dp[i][1]=max(dp[i−1][1],dp[i−1][0]−prices[i])

  • 对于第 iii 日处于第一次卖出后,即 dp[i][2]dp[i][2]dp[i][2],可能由以下情况推导而来:

    • 前面已经卖出,当日未进行任何操作:dp[i−1][2]dp[i-1][2]dp[i−1][2]
    • 当日卖出:dp[i−1][1]+prices[i]dp[i-1][1]+prices[i]dp[i−1][1]+prices[i]

    则:dp[i][2]=max(dp[i−1][2],dp[i−1][1]+prices[i])dp[i][2]=max(dp[i-1][2],dp[i-1][1]+prices[i])dp[i][2]=max(dp[i−1][2],dp[i−1][1]+prices[i])

  • 同理,dp[i][3]=max(dp[i−1][3],dp[i−1][2]−prices[i])dp[i][3]=max(dp[i-1][3],dp[i-1][2]-prices[i])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[i][4]=max(dp[i-1][4],dp[i-1][3]+prices[i])dp[i][4]=max(dp[i−1][4],dp[i−1][3]+prices[i])

代码

class Solution {public:int maxProfit(vector<int>& prices) {int lens = prices.size();vector<vector<int>> dp(lens, vector<int> (5));dp[0][1] = -prices[0], dp[0][3] = -prices[0];for (int i=1; i<lens; ++i) {dp[i][0] = dp[i-1][0];dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i]);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]);}return dp[lens - 1][4];}
};

优化代码

class Solution {public:int maxProfit(vector<int>& prices) {int fstBuy = INT_MIN, fstSell = 0;int secBuy = INT_MIN, secSell = 0;for (int p: prices) {fstBuy = max(fstBuy, -p);fstSell = max(fstSell, fstBuy + p);secBuy = max(secBuy, fstSell - p);secSell = max(secSell, secBuy + p);}return secSell;}
};

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

上一题的一般化,分好奇偶即可:

class Solution {public:int maxProfit(int k, vector<int>& prices) {if (prices.empty()) return 0;int lens = prices.size();int wide = 2 * k + 1;vector<vector<int>> dp(lens, vector<int> (wide, 0));for (int j=0; j<wide; ++j) if (j % 2 == 1) dp[0][j] = -prices[0];for (int i=1; i<lens; ++i) {dp[i][0] = dp[i-1][0];for (int j=1; j<wide; ++j) {if (j % 2 == 1) dp[i][j] = max(dp[i-1][j], dp[i-1][j-1] - prices[i]);else if (j % 2 == 0) dp[i][j] = max(dp[i-1][j], dp[i-1][j-1] + prices[i]);}}return dp[lens - 1][wide - 1];}
};

309. 最佳买卖股票时机含冷冻期

dp数组含义

本题中每天可能的状态有三种:

dp[i][j]dp[i][j]dp[i][j] 表示第 iii 天结束后,在第 jjj 中状态下的最大收益

递推公式

  • 当日结束后为持有股票状态时,即 dp[i][0]dp[i][0]dp[i][0] ,可能由以下情况推导而来:

    • 当日买入股票,今天不能是冷冻期, dp[i−1][2]−prices[i]dp[i-1][2]-prices[i]dp[i−1][2]−prices[i]
    • 之前就已经买入了股票,当日未进行操作:dp[i−1][0]dp[i-1][0]dp[i−1][0]

    则,dp[i][0]=max(dp[i−1][0],dp[i−1][2]−prices[i])dp[i][0]=max(dp[i-1][0],dp[i-1][2]-prices[i])dp[i][0]=max(dp[i−1][0],dp[i−1][2]−prices[i])

  • 当日卖出,即 dp[i][1]dp[i][1]dp[i][1]

    • 今天卖出,前一天必为持股状态,dp[i−1][0]+prices[i]dp[i-1][0]+prices[i]dp[i−1][0]+prices[i]

    则,dp[i][1]=dp[i−1][0]+prices[i]dp[i][1]=dp[i-1][0]+prices[i]dp[i][1]=dp[i−1][0]+prices[i]

  • 非当日卖出,即 dp[i][2]dp[i][2]dp[i][2],可能由以下情况推导而来:

    • 前一日卖出,今日冷冻期,dp[i−1][1]dp[i-1][1]dp[i−1][1]
    • 非前一日卖出,今日非冷冻期:dp[i−1][2]dp[i-1][2]dp[i−1][2]

    则 dp[i][2]=max(dp[i−1][1],dp[i−1][2])dp[i][2]=max(dp[i-1][1],dp[i-1][2])dp[i][2]=max(dp[i−1][1],dp[i−1][2])

初始化:除了第一天就买股票 dp[0][0]=−prices[0]dp[0][0]=-prices[0]dp[0][0]=−prices[0] 外全 0。

遍历顺序:从前到后即可。

代码

class Solution {public:int maxProfit(vector<int>& prices) {int lens = prices.size();vector<vector<int>> dp(lens, vector<int> (3, 0));dp[0][0] = -prices[0];for (int i=1; i<lens; ++i) {dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i]);dp[i][1] = dp[i-1][0] + prices[i];dp[i][2] = max(dp[i-1][2], dp[i-1][1]);}return max(dp[lens - 1][1], dp[lens - 1][2]);}
};

714. 买卖股票的最佳时机含手续费

就加个手续费,和 122 区别不大。

dp数组含义

本题中每一天结束后有两种状态:持有股票和不持有股票

递推公式

  • 对于第 iii 天结束后,处于持股状态,即 dp[i][0]dp[i][0]dp[i][0],可能由两种状态推导得到:

    • 前面买入,当日未进行操作:dp[i−1][0]dp[i-1][0]dp[i−1][0]
    • 当日买入,并支付手续费 dp[i−1][1]−prices[i]−feedp[i-1][1]-prices[i]-feedp[i−1][1]−prices[i]−fee

    则:dp[i][0]=max(dp[i−1][0],dp[i−1][1]−prices[i]−fee)dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]-fee)dp[i][0]=max(dp[i−1][0],dp[i−1][1]−prices[i]−fee)

  • 对于第 iii 天结束后,处于未持股状态,即 dp[i][1]dp[i][1]dp[i][1],可能由两种状态推导得到:

    • 一直未买入或前面已卖出,当日未进行操作:dp[i−1][1]dp[i-1][1]dp[i−1][1]
    • 当日卖:dp[i−1][0]+prices[i]dp[i-1][0]+prices[i]dp[i−1][0]+prices[i]

    则:dp[i][1]=max(dp[i−1][1],dp[i−1][0]+prices[i])dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i])dp[i][1]=max(dp[i−1][1],dp[i−1][0]+prices[i])

代码

class Solution {public:int maxProfit(vector<int>& prices, int fee) {int lens = prices.size();vector<vector<int>> dp(lens, vector<int> (2));dp[0][0] = -prices[0] - fee;for (int i=1; i<lens; ++i) {dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i] - fee);dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i]);}return dp[lens - 1][1];}
};

LeetCode上的各种股票最大收益相关推荐

  1. c语言股票最大收益_长期持有指数基金是最好的选择?指数基金的历史年化收益率是多少?...

    原创 价值人生 life198012 2018年沪深市场下跌到了历史底部区域,P2P的暴雷潮甚至连累到了一些债券基金.2018年的年报,一些公司的商誉也开始了暴雷.公司可能跌没了,而指数基金是永续的. ...

  2. LSTM对股票的收益进行预测(Keras实现)

    目录 一.概述: 二.股票数据准备 三.股票数据预处理 1.数据特征归一化(标准化) 2.将数据集转化为有监督学习问题 四.股票数据划分为训练集和测试集 五.模型构建及其预测 1.搭建LSTM模型并绘 ...

  3. 聚宽 get_price 多个股票数据_《实证资产定价:股票横截面收益》

    本书简介:<实证资产定价:股票横截面收益>是对实证资产定价研究领域重要的成果的全面综述.首先,本书通过对详细案例所示结果的实施和解释的深入讨论,对当下广泛使用的计量经济学方法进行了全面阐述 ...

  4. 技术图文:如何在leetcode上进行算法刻意练习?

    背景 众所周知,通过刻意练习高质量的题目可以加深我们对计算机科学中经典数据结构的深刻理解,从而可以快速用合适的数据结构去解决现实中的问题.而LeetCode就是一个收集了各大IT公司的笔试面试题的在线 ...

  5. LeetCode上求两个排序数组中位数问题—— Median of Two Sorted Arrays

    1.题目 There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of th ...

  6. Leetcode上的解法看不懂?试着用动画的方式去辅助理解

    推荐一个用动画的方式演示leetcode题目解题思路的github仓库: https://github.com/MisterBooo/LeetCodeAnimation 超过15000个star: 用 ...

  7. sum 去重_总结leetcode上【排列问题】【组合问题】【子集问题】回溯算法去重的两种写法!...

    本周小结!(回溯算法系列三)续集 在 本周小结!(回溯算法系列三) 中一位录友对 整颗树的本层和同一节点的本层有疑问,也让我重新思考了一下,发现这里确实有问题,所以专门写一篇来纠正,感谢录友们的积极交 ...

  8. 浅谈深度学习:LSTM对股票的收益进行预测(Sequential 序贯模型,Keras实现)

    浅谈深度学习:LSTM对股票的收益进行预测(Sequential 序贯模型,Keras实现) 总包含文章: 一个完整的机器学习模型的流程 浅谈深度学习:了解RNN和构建并预测 浅谈深度学习:基于对LS ...

  9. leetcode上奇怪的解答错误

    最近在leetcode上刷题,有一题的错误很奇怪,题目如下 解答的代码和报错如下: 图片: 代码: class Solution { public:void reverse(string& s ...

  10. 自学Scala的第四天——去leetcode上刷刷题,顺便学学基础语法

    通过漫山遍野的寻找,依旧找不到学习scala的方向,突然想到了leetcode,不知道上面支不支持scala,记得最早学习java时候,是在上面一顿刷题,一顿操作,先不说别的,至少语法什么的是学到了不 ...

最新文章

  1. 学习如何看懂SQL Server执行计划(三)——连接查询篇
  2. javascript string replace 正则替换
  3. Latex排版全解(转)
  4. 硅谷大佬提前剧透未来!6本书,助你走在AI时代前沿
  5. linux xorg 文件 位置,Linux系统中xorg.conf文件详细介绍
  6. dajngo3设置静态文件访问
  7. 腾讯回应多闪弹窗事件;京东要求员工梳理亲戚同学关系;雷军董明珠十亿赌局胜负已定 | 极客头条...
  8. python笑傲江湖统计字数_Udacity.深度学习.用 Python 统计字数.2017-10-30
  9. matlab仿真二元等幅边射阵,MATLAB仿真天线阵代码.doc
  10. 【概念理论】不存在的NOIP2016
  11. oracle 前导列_Oracle数据库表和表列讲解
  12. 泰坦尼克号数据_kaggle泰坦尼克号之Python手把手数据分析
  13. 使用NLPIR汉语分词系统进行分词
  14. 20210311 plecs 对传递函数进行波特图分析
  15. 六种Web身份验证方法比较和Flask示例代码
  16. DLL文件如何还原打开方式
  17. android 调用系统文件管理器(打开手机自带的文件管理器)
  18. 日文电脑文件路径问题
  19. 为什么不建议把数据库部署在 Docker 容器内?
  20. 英语电影观后感之角斗士

热门文章

  1. 用html计算长方形的面积公式,长方形面积公式是什么
  2. 博士申请 | 美国北卡州立大学胥栋宽老师招收深度学习方向全奖博士/硕士/博后...
  3. O-Growing Mushrooms
  4. linux用户是什么意思,Linux中的./是什么意思
  5. ubuntu16.04安装caffe
  6. [项目管理-2]:软硬件项目管理 - 干系人管理、实践活动、常见工具
  7. 在mybatis中怎么书写工具类,也就是创建一个sqlsessionFactory
  8. 在PS中,1PX等于多少毫米?
  9. android打飞机游戏、MVP句子迷App、悬浮窗、RxJava+Retrofit、加载动画、定制计划App等源码...
  10. 树莓派linux led字符设备驱动( platform)