动态规划-买卖股票的最佳时机 专题
动态规划-买卖股票的最佳时机 专题
买卖股票的最佳时机(可以用贪心)
力扣题目链接
因为只能买卖一次。
dp[i][j]
数组的含义:第 i 天状态为 j 时的最大现金。
递推公式:
设dp[i][0]
为持有股票状态, dp[i][1]
为不持有股票状态。
当 该状态是持有股票状态:
- 如果当天还是保持持有股票状态,那么保持原状,
dp[i][0]
=dp[i-1][0]
; - 如果当天选择买入股票,所得现金就是买入今天的股票后所得现金即:
-prices[i]
那么选取最大的即可:dp[i][0]
= Math.max(dp[i-1][0]
, -prices[i]
);
当 该状态是不持有股票状态:
- 如果当天还是保持不持有股票状态,那么保持原状,
dp[i][1]
=dp[i-1][1]
; - 如果当天是卖出股票状态,那么最大现金就是
dp[i][1]
=dp[i-1][0]
+prices[i]
(即 持有股票状态的最大现金dp[i-1][0]
+ 今天的股票价格prices[i]
)
那么选取最大的即可:dp[i][1]
= Math.max(dp[i-1][1]
, dp[i-1][0]
+ prices[i]
);
// 动归
public int maxProfit1(int[] prices) {int[][] dp = new int[prices.length][2];int result = 0;dp[0][0] = -prices[0];dp[0][1] = 0;for (int i = 1; i < prices.length; i++) {dp[i][0] = Math.max(dp[i - 1][0], -prices[i]);dp[i][1] = Math.max(dp[i - 1][1], prices[i] + dp[i - 1][0]);}return dp[dp.length - 1][1];
}
/*
时间复杂度:O(n)
空间复杂度:O(n)
*/// 贪心
public int maxProfit(int[] prices) {// 找到一个最小的购入点int low = Integer.MAX_VALUE;// res不断更新,直到数组循环完毕int res = 0;for(int i = 0; i < prices.length; i++){low = Math.min(prices[i], low);res = Math.max(prices[i] - low, res);}return res;}
/*
时间复杂度:O(n)
空间复杂度:O(1)
*/
买卖股票的最佳时机2(可以用贪心)
力扣题目链接
可以多次买卖股票
因为这里的股票可以买卖多次了,那么和第一个的唯一区别就是在递推公式上
在持有股票的状态上:dp[i][0]
- 假如当天是买入股票,那么就是
dp[i][0]
=dp[i-1][1]
-prices[i]
(即 不持有股票状态的最大现金dp[i][1]
- 当天的股票价格prices[i]
)
// 动归
public int maxProfit(int[] prices) {int[][] dp = new int[prices.length][2];int result = 0;dp[0][0] = -prices[0];dp[0][1] = 0;for (int i = 1; i < prices.length; i++) {dp[i][0] = Math.max(dp[i - 1][0], dp[i-1][1] - prices[i]);dp[i][1] = Math.max(dp[i - 1][1], prices[i] + dp[i - 1][0]);}return dp[dp.length - 1][1];}
/*
时间复杂度:O(n)
空间复杂度:O(n)
*/// 贪心
public int maxProfit(int[] prices) {int result = 0;for (int i = 1; i < prices.length; i++) {result += Math.max(prices[i] - prices[i - 1], 0);}return result;
}
/*
时间复杂度:O(n)
空间复杂度:O(1)
*/
买卖股票的最佳时机3
力扣题目链接
最多可以完成两笔交易 ,意味着可以买卖一次,可以买卖两次,可以不买卖。
两笔交易,也就是最多四个状态。即 第一次买入,第一次卖出,第二次买入,第二次卖出
dp[i][j]
数组的含义:在第 i 天状态为 j 的时候的所得最大金钱。
递推公式:
在第一次持有股票状态下:dp[i][1]
- 保持持有股票的状态,即
dp[i][1]
=dp[i-1][1]
- 第一次买入股票,即
dp[i][1]
=-prices[i]
dp[i][1]
= Math.max( dp[i-1][1]
,-prices[i]
);
在第一次不持有股票状态下:dp[i][2]
- 保持不持有股票的状态,即
dp[i][2]
=dp[i-1][2]
- 第一次卖出股票,
dp[i][2]
=dp[i-1][1] + prices[i]
dp[i][2]
= Math.max(dp[i-1][1] + prices[i]
, dp[i-1][2]
)
在第二次持有股票状态下:dp[i][3]
- 当天保持持有股票,即
dp[i][3]
=dp[i-1][3]
- 当天买入股票,就要看第一次不持有股票时的最大金钱了,即
dp[i][3]
=dp[i-1][2]
-prices[i]
dp[i][3]
= Math.max(dp[i-1][2] - prices[i]
, dp[i-1][3]
)
在第二次不持有股票状态下:dp[i][4]
- 保持不持有股票的状态,即
dp[i][4]
=dp[i-1][4]
- 当天卖出股票,就需要看第二次持有股票时的最大金钱了,即
dp[i][4]
=dp[i-1][3]
+prices[i]
dp[i][4]
= Math.max(dp[i-1][3] + prices[i]
, dp[i-1][4]
)
初始化(结合这个dp数组的含义去想):
dp[0][0]
= 0 (可以省略,因为上述都没有设置 没有操作 的这个选择)
dp[0][1]
= -prices[0]
dp[0][2]
= 0
dp[0][3]
= -prices[0]
dp[0][4]
= 0
public int maxProfit(int[] prices) {int len = prices.length;if (prices.length == 0) return 0;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] + prices[i]);dp[i][3] = Math.max(dp[i - 1][3], dp[i][2] - prices[i]);dp[i][4] = Math.max(dp[i - 1][4], dp[i][3] + prices[i]);}return dp[len - 1][4];}
/*
时间复杂度:O(n)
空间复杂度:O(n*5)
*/
买卖股票的最佳时机4
力扣题目链接
最多可以完成K笔交易 ,是时机3的进阶版
dp[i][j]
数组含义依旧。
参考时机3的做法:
0:表示不操作;
1:表示第一次买入;
2:表示第一次卖出;
3:表示第二次买入;
4:表示第二次卖出;
。。。。 可以发现,除了0以外,奇数次为买入股票,偶数次为卖出股票。
题目要求的是至多 K 笔交易,j 的定义范围就是到了 2 * K + 1即可。即 int[][] dp = new int[prices.length][2 * K + 1]
递推公式:
奇数次: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])
public int maxProfit(int k, int[] prices) {if (k < 1 && prices.length == 0) return 0;int[][] dp = new int[prices.length][2 * k + 1];for (int i = 1; i < 2 * k; i += 2) {dp[0][i] = -prices[0];}for (int i = 1; i < prices.length; i++) {for (int j = 0; j < 2 * k - 1; 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[prices.length - 1][2 * k];}
/*
时间复杂度:O(n)
空间复杂度:O(n*k)
*/
买卖股票的最佳时机含冷冻期
力扣题目链接
可以进行多次的买卖股票,但是卖出股票的第二天不能有其他操作。
dp数组含义依旧。
递推公式:
状态一:当持有股票状态:保持原状(前些天买的股票,今天维持;或者是当天买入股票) dp[i][0]
当不持有股票状态:两种卖出股票状态
- 状态二:当天依旧是保持卖出股票状态(前两天卖出股票,度过一天冷冻期;前一天卖出股票状态,一直没操作)
dp[i][1]
- 状态三:当天卖出股票
dp[i][2]
状态四:今天为冷冻期,不能操作股票,只有一天。 dp[i][3]
为什么在之前的做题中「今天卖出股票」没有单独列为一个状态,而不是归类于「今天不持有股票」状态?
因为冷冻期的前一天一定是「今天卖出股票」状态,而不能是「今天卖不持有股票」状态,毕竟「今天卖不持有股票」状态也包含了「今天卖出股票」状态,但是不一定是「今天卖出股票」。
所以递推公式为:
状态一: dp[i][0]
- 操作一:前一天就是持有股票状态,今日继续保持
dp[i][0]
=dp[i-1][0]
- 操作二:今日买入了股票,有两种情况:
- 前一天是冷冻期(状态四):
dp[i-1][3]
-prices[i]
- 前一天是保持卖出股票状态:
dp[i-1][1]
-prices[i]
- 前一天是冷冻期(状态四):
dp[i][0]
= Math.max( dp[i-1][0]
,Math.max( dp[i-1][3]
- prices[i]
, dp[i-1][1]
- prices[i]
))
状态二: dp[i][1]
- 前一天卖出股票状态,一直没操作(状态二)
dp[i][1]
=dp[i-1][1]
- 前两天卖出股票,度过一天冷冻期
dp[i][1]
=dp[i-1][3]
dp[i][1]
= Math.max( dp[i-1][1]
,dp[i-1][3]
)
状态三:dp[i][2]
- 前一天持有股票状态
dp[i-1][0]
+prices[i]
dp[i][2]
= dp[i-1][0]
+ prices[i]
状态四:dp[i][3]
dp[i][3]
= dp[i-1][2]
public int maxProfit(int[] prices) {int n = prices.length;if (n == 0) return 0;int[][] dp = new int[n][4];dp[0][0] -= prices[0]; // 持股票for (int i = 1; i < n; i++) {dp[i][0] = Math.max(dp[i-1][0],Math.max(dp[i-1][3] - prices[i] , dp[i-1][1] - prices[i]));dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][3]);dp[i][2] = dp[i - 1][0] + prices[i];dp[i][3] = dp[i - 1][2];}return Math.max(dp[n - 1][3], Math.max(dp[n - 1][1], dp[n - 1][2]));}/*
时间复杂度:O(n)
空间复杂度:O(n)
*/
买卖股票的最佳时机含手续费
力扣题目链接
其实这题和最佳时机2相似,只不过多了个需要交手续费的步骤而已。
所以在递推公式上,只需要改变以下的步骤:
在不持有股票的状态下:
- 假如卖出股票,那么所得的最大现金为:
dp[i][1]
=dp[i-1][0]
+prices[i]
-fee
);
最大现金为:dp[i][1]
= Math.max(dp[i-1][1]
, dp[i-1][0]
+ prices[i]
- fee
);
// 动归
public int maxProfit(int[] prices, int fee) {int len = prices.length;int[][] dp = new int[len][2];dp[0][0] = -prices[0];for (int i = 1; i < len; i++) {dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i]);dp[i][1] = Math.max(dp[i - 1][0] + prices[i] - fee, dp[i - 1][1]);}return Math.max(dp[len - 1][0], dp[len - 1][1]);
}
/*
时间复杂度:O(n)
空间复杂度:O(n)
*/
总结:
抓住以下问题来解决该专题。
dp[i][j]
数组的含义;
利用二维数组解决该类问题:一个表示持有,一个表示不持有,且 持有 != 当天购买,购买股票 == 》持有;
将每天的每个股票状态进行分析;
递推公式;
动态规划-买卖股票的最佳时机 专题相关推荐
- 动态规划---买卖股票的最佳时机
说明 股票问题算是动态规划问题中的一个小分支,这里单独写一篇文章介绍.至于动态规划基础问题和详细的处理步骤我在我的另一篇文章中详细介绍过.具体解决步骤请移步观看--动态规划基础篇.如果想了解01背包问 ...
- 贪心/动态规划 - 买卖股票的最佳时机含手续费
题目链接 贪心 每次只能交易一个股票,最优的选择就是低买高卖.不过每次交易股票都有一次手续费.可以把手续费算到买入的价格里.只要能收益就交易. 每次卖出一个股票就拥有了原价购买股票的机会.这样就能在具 ...
- 动态规划——买卖股票的最佳时机含手续费
题目 给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 :整数 fee 代表了交易股票的手续费用. 你可以无限次地完成交易,但是你每笔交易都需要付手续费.如果你已经购买 ...
- 动态规划 - 买卖股票的最佳时机 IV
题目链接 使用两个辅助数组 buy[i][j]buy[i][j]buy[i][j]表示前iii个股票发生jjj次交易且手上有一只股票的最大收益. sell[i][j]sell[i][j]sell[i] ...
- 动态规划 - 买卖股票的最佳时机 III
每天结束,一共有5种状态: 没有操作(可以不记录,收益为0) buy1:第一次买入 sell1:第一次卖出(完成一次交易) buy2:第二次买入 sell2:第二次卖出(完成两次交易) 状态转移: b ...
- 力扣贪心算法专题(一)455.分发饼干 376. 摆动序列 53. 最大子序和 122.买卖股票的最佳时机II 1005.K次取反后最大化的数组和 思路及C++实现 贪心算法 动态规划
文章目录 贪心算法 455.分发饼干 思路 步骤 代码 376. 摆动序列 贪心算法 思路 分析 代码 动态规划 思路 步骤 代码 53. 最大子序和 暴力解法 双层for循环 贪心算法 思路 分析 ...
- LeetCode 188. 买卖股票的最佳时机 IV(动态规划)
1. 题目 给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你最多可以完成 k 笔交易. 注意: 你不能同时参与多笔交易(你必须在再次购买 ...
- LeetCode 123. 买卖股票的最佳时机 III(动态规划)
1. 题目 给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你最多可以完成 两笔 交易. 注意: 你不能同时参与多笔交易(你必须在再次购买 ...
- leetcode 714 买卖股票的最佳时机含手续费-动态规划(中等)
714 买卖股票的最佳时机含手续费-动态规划(中等) 给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 :非负整数 fee 代表了交易股票的手续费用. 你可以无限次地完成交 ...
最新文章
- android canvas_Android自定义View之绘制虚线
- python-进程、线程
- 孩子数学成绩不好怎么办_孩子数学成绩不好怎么办
- mysql 数据库名称限制_mysql 数据库名称限制
- CRY ENGINE 3 引擎详解
- 解决JS文件页面加载时的阻塞
- 数据集:高考录取分数
- VC “变速齿轮”再研究
- matlab心电显示,请问如何在GUI界面中打开并显示心电信号
- 电脑系统故障维修,系统C盘满了怎么办?教你c盘清理方法
- Excel的选取和函数常用技巧及快捷键(一)
- muduo学习笔记:net部分之Http--HttpServer
- 网站倒计时使用服务器时间,根据服务器时间校准倒计时时间
- 如何备份VMware虚拟磁盘文件并移植到其他虚拟机
- 记录IDEA导包不能导入的问题
- visio画图-----如何克服两箭头交叉变形 及 箭头自动重绘?
- AMBA CHI协议学习笔记2-Link层
- Mysql的两阶段锁协议
- Vert.x入坑须知(3)
- 如何关闭vivado的Jtag自动检测