【Leetcode刷题篇/面试篇】经典动态规划-买卖股票问题总结汇总
发布日期:2021-06-29 15:35:13 浏览次数:2 分类:技术文章

本文共 6331 字,大约阅读时间需要 21 分钟。

经典动态规划-买卖股票问题

一、一次买卖股票-

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

注意:你不能在买入股票前卖出股票。

示例 1:

输入: [7,1,5,3,6,4]

输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入: [7,6,4,3,1]

输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

解题思路:维护一个变量存储当前天之前的最小股票价,计算维护最大利润即可。

class Solution {
public int maxProfit(int[] prices) {
// 判断数组 if(prices==null|| prices.length==0 || prices.length==1){
return 0; } int profit = 0; int min_prices = prices[0]; for(int i=1;i

二、多次买卖股票(不能同时参与多笔交易)-

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

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

示例 1:

输入: [7,1,5,3,6,4]

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

示例 2:

输入: [1,2,3,4,5]

输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

输入: [7,6,4,3,1]

输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

解题思路:考虑到【不能同时参与多笔交易】,因此每天交易结束后只可能存在手里有一只股票或者没有股票的状态。

dp0:没有股票的状态

dp1:有股票的状态

股票转移的方程:

dp0 Math.max(前一天没有股票的,前一天有股票卖掉了)

dp1 Math.max(前一天有股票,前一天没有股票买了一个)

class Solution {
public int maxProfit(int[] prices) {
int dp0 = 0; int dp1 = -prices[0]; for(int i=1;i

三、多次买卖股票(不能同时参与多笔交易)且包含冷却期-

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

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

卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

示例:

输入: [1,2,3,0,2]

输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

解题思路:有三种不同的状态:

  • 持有一支股票; 初始化-price[0]
  • 不持有股票,并且处于冷冻期 初始化0
  • 不持有股票,并且不处于冷冻期 初始化0

对于转移方程来说:

  • 持有一只股票

    持有的这一只股票可能是i-1天就已经持有了;也可能是第i天买入的,那么i-1天就不能持有股票且不处于冷冻期,那么

    dp0= Math.max(前一天就持有了,前一天没有持有且不处于冷冻期 - 买来的)

  • 不持有一只股票且处于冷冻期

    持有股票且处于冷冻期表明今天卖出了,那么前一天必须持有股票 + 卖出的

    dp1=(前一天持有的股票dp0 +卖掉了)

  • 不持有一只股票且不处于冷冻期

    不持有股票且不处于冷冻期说明当天没操作,看前一天持有股票;

    看前一天持有卖出股票

    dp2=Math.min(前一天就是未持有冷冻dp1,前一天持有未冷冻期dp12)

最终结果就是看不持有股票中的最大值

class Solution {
public int maxProfit(int[] prices) {
if (prices.length == 0) {
return 0; } // 三个状态 // 持有 int dp0 = -prices[0]; // 未持有冷冻期 int dp1 = 0; // 未持有不冷冻期 int dp2 = 0; for(int i=1;i

四、多次买卖股票(不能同时参与多笔交易)且包含手续费-

给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

示例 1:

输入: prices = [1, 3, 2, 8, 4, 9], fee = 2

输出: 8
解释: 能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.

注意:

0 < prices.length <= 50000.

0 < prices[i] < 50000.
0 <= fee < 50000.

解题思路:

两个变量:cash和hold分别是不持有股票的最大利润 持有股票的最大利润

转移方程:

cash不持有股票的最大利润:前一天本来就没有持有cash/hold 前一天持有了 今天卖了prices[i] 手续费fee

hold持有股票的最大利润:前一天就持有了hold/前一天没有持有 今天买入了cash - prices[i]

package com.lcz.leetcode;// 买卖股票的最佳时机含手续费public class Leetcode714 {
class Solution {
public int maxProfit(int[] prices, int fee) {
if(prices.length==0){
return 0; } // 持有和未持有 // 未持有 int cash = 0; // 持有 int hold = -prices[0]; for(int i=1;i

五、多次买卖股票(不能同时参与多笔交易)且最多只能进行两次交易-

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

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

示例 1:

输入: [3,3,5,0,0,3,1,4]

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

示例 2:

输入: [1,2,3,4,5]

输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

输入: [7,6,4,3,1]

输出: 0
解释: 在这个情况下, 没有交易完成, 所以最大利润为 0。

解题思路:5种状态

dp0 未交易

dp1 买入一次

dp2 卖出一次

dp3 买入两次

dp4 卖出两次

转移方程:

dp0 一直为0

dp1 前一天买入今天未操作/前一天未买入今天 买入

dp2 前一天已卖出/前一天买入 今天卖出

dp3 前一天买入两次的/前一天卖出的今天买入的

dp4 前一天卖出的/前一天买入的今天卖出的

结果:卖出1次或卖出两次中的最大收益

class Solution {
public int maxProfit(int[] prices) {
// 数组判断 if(prices==null||prices.length==1||prices.length==0) {
return 0; } // 初始化 // 未交易 int dp0 = 0; // 买入一次 int dp1 = -prices[0]; // 卖出一次 int dp2 = Integer.MIN_VALUE; // 买入两次 int dp3 = Integer.MIN_VALUE; // 卖出两次 int dp4 = Integer.MIN_VALUE; // 遍历 for(int i=1;i

六、多次买卖股票(不能同时参与多笔交易)且最多只能进行K次交易-

给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 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 。

提示:

0 <= k <= 109

0 <= prices.length <= 1000
0 <= prices[i] <= 1000

初始化:

如果我们把买+卖算一次交易

那么这道题的限制是交易数,天数,手上有没有股票(有股票就不能买,没有股票就不能卖)
建立三维的数组:dp [k] [i] [2]
其中k表示的是交易次数,i表示的是第几天,2表示的是有两种状态:手上没有股票/手上有股票
dp [k] [i] [0] 数组中的数字表示的是到了第i天,交易了k次,手上没有股票获得的最大利益
dp [k] [i] [1] 数组中的数字表示的是到了第i天,交易了k次,手上持有股票获得的最大利益

在我们写转移方程之前我们还要先考虑之前提出来的问题–交易数怎么算,买+卖是一次交易,我的t表示的是交易次数,那我到底是买入的时候t要加一了,还是卖出的时候t才加一?

答:都可以,不同的定义就会有不同的实现

来看一下两者有什么不同

买入就算一次交易:
i天t次交易现在手上不持有 = max(i-1天t次交易手上不持有,i-1天t次交易手上持有 + i天卖出价格prices)
dp[t] [i] [0] = max(dp[t] [i - 1] [0], dp[t] [i - 1] + prices[i]);

i天t次交易现在手上持有 = max(i-1天t次交易手上持有,i-1天t-1次交易手上不持有 - i天买入价格)

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

初始化

dp[t] [0] [0] 0天t次交易,手上不持有:可能的 0

dp[t] [0] [1] 0天t次交易,手上持有:不可能(0天没有股票,所以无法买入持有;持有说明至少进行了一次买入,买入就交易,因此这里不可能【不可能意思就是不能从这里转移】
dp[0] [i] [0] i天0次交易,手上不持有:0
dp[0] [i] [1] i天0次交易,手上持有:不可能(不交易手上不可能持有)

class Solution {
public int maxProfit(int k, int[] prices) {
if(prices==null||prices.length==0||prices.length==1) {
return 0; } // 判断k次交易 if(k>(prices.length/2)) {
int sum = 0; for(int i=1;i
0?temp:0); } return sum; } // 动态数组 int[][][] dp = new int[k+1][prices.length+1][2]; // 初始化 交易 0天t次交易 手上持有 不可能 for(int t=0;t<=k;t++) {
dp[t][0][1] = Integer.MIN_VALUE; } // i天0次交易手上持有 不可能 for(int i=0;i

转载地址:https://codingchaozhang.blog.csdn.net/article/details/111309045 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:【Java锁体系】五、隐式锁和显式锁的区别(Synchronized和Lock的区别)
下一篇:【Leetcode刷题篇】leetcode188 买卖股票的最佳时机IV

发表评论

最新留言

留言是一种美德,欢迎回访!
[***.207.175.100]2024年04月17日 16时54分55秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章