代码随想录Day42 188.买卖股票的最佳时机IV,309.最佳买卖股票时机含冷冻期,本周小结动态规划,714.买卖股票的最佳时机含手续费,Leetcode股票问题总结篇!

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

力扣题目链接(opens new window)

给定一个整数数组 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 <= 100
  • 0 <= prices.length <= 1000
  • 0 <= prices[i] <= 1000

思路

这道题目可以说是动态规划:123.买卖股票的最佳时机III (opens new window)的进阶版,这里要求至多有k次交易。

动规五部曲,分析如下:

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

动态规划:123.买卖股票的最佳时机III (opens new window)中,我是定义了一个二维dp数组,本题其实依然可以用一个二维dp数组。

使用二维数组 dp[i][j] :第i天的状态为j,所剩下的最大现金是dp[i][j]

j的状态表示为:

  • 0 表示不操作
  • 1 第一次买入
  • 2 第一次卖出
  • 3 第二次买入
  • 4 第二次卖出
  • .....

大家应该发现规律了吧 ,除了0以外,偶数就是卖出,奇数就是买入

题目要求是至多有K笔交易,那么j的范围就定义为 2 * k + 1 就可以了。

  1. 确定递推公式

还要强调一下:dp[i][1],表示的是第i天,买入股票的状态,并不是说一定要第i天买入股票,这是很多同学容易陷入的误区

达到dp[i][1]状态,有两个具体操作:

  • 操作一:第i天买入股票了,那么dp[i][1] = dp[i - 1][0] - prices[i]
  • 操作二:第i天没有操作,而是沿用前一天买入的状态,即:dp[i][1] = dp[i - 1][1]

选最大的,所以 dp[i][1] = max(dp[i - 1][0] - prices[i], dp[i - 1][1]);

同理dp[i][2]也有两个操作:

  • 操作一:第i天卖出股票了,那么dp[i][2] = dp[i - 1][1] + prices[i]
  • 操作二:第i天没有操作,沿用前一天卖出股票的状态,即:dp[i][2] = dp[i - 1][2]

所以dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2])

本题和动态规划:123.买卖股票的最佳时机III (opens new window)最大的区别就是这里要类比j为奇数是买,偶数是卖的状态

  1. dp数组如何初始化

第0天没有操作,这个最容易想到,就是0,即:dp[0][0] = 0;

第0天做第一次买入的操作,dp[0][1] = -prices[0];

第0天做第一次卖出的操作,这个初始值应该是多少呢?

此时还没有买入,怎么就卖出呢? 其实大家可以理解当天买入,当天卖出,所以dp[0][2] = 0;

第0天第二次买入操作,初始值应该是多少呢?应该不少同学疑惑,第一次还没买入呢,怎么初始化第二次买入呢?

第二次买入依赖于第一次卖出的状态,其实相当于第0天第一次买入了,第一次卖出了,然后在买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少。

所以第二次买入操作,初始化为:dp[0][3] = -prices[0];

第二次卖出初始化dp[0][4] = 0;

所以同理可以推出dp[0][j]当j为奇数的时候都初始化为 -prices[0]

在初始化的地方同样要类比j为偶数是卖、奇数是买的状态

  1. 确定遍历顺序

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

  1. 举例推导dp数组

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

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

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

三维数组

public class Best_Time_to_Buy_and_Sell_StockIV {
    public int maxProfit1(int k, int[] prices) {
        if (prices.length == 0) return 0;
        int len = prices.length;
        int[][][] dp = new int[len][k + 1][2];//dp 是一个三维数组,其中 dp[i][j][0] 表示第 i 天结束时第 j 次交易不持有股票的最大利润,dp[i][j][1] 表示第 i 天结束时第 j 次交易持有股票的最大利润。
        for (int i = 0; i <= k; i++) {
            dp[0][i][1] = -prices[0];//初始化 dp[0][j][1] 为 -prices[0],表示第0天买入第j次股票。
        }
        for (int i = 1; i < len; i++) {//遍历价格数组,对于每个价格,更新 dp[i][j][0] 和 dp[i][j][1]:dp[i][j][0]:选择不持有股票或者在今天卖出股票,取最大值。dp[i][j][1]:选择不买入股票或者在今天买入股票,取最大值,且必须在今天之前卖出过股票。
            for (int j = 1; j <= k; j++) {
                dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
                dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
            }
        }
        return dp[len - 1][k][0];//返回最后一天不持有股票的最大利润 dp[len - 1][k][0]。
    }
  =
}
  • 时间复杂度:O(k * len),其中len是数组prices的长度,k是允许的交易次数。需要遍历每一天和每一次交易。
  • 空间复杂度:O(k * len),需要一个大小为len×(k+1)×2的三维数组来存储每一天、每一次交易的状态。

二维数组

public class Best_Time_to_Buy_and_Sell_StockIV {
    public int maxProfit2(int k, int[] prices) {
        if (prices.length == 0) return 0;
        int len = prices.length;
        int[][] dp = new int[len][k*2 + 1];//dp 是一个二维数组,其中 dp[i][j] 表示第 i 天结束时第 j 次交易的最大利润。
        for (int i = 1; i < k*2; i += 2) {
            dp[0][i] = -prices[0];//初始化 dp[0][j] 为 -prices[0],表示第0天买入第j次股票。
        }
        for (int i = 1; i < len; i++) {//遍历价格数组,对于每个价格,更新 dp[i][j]:dp[i][j + 1]:选择不买入股票或者在今天买入股票,取最大值。dp[i][j + 2]:选择不卖出股票或者在今天卖出股票,取最大值。
            for (int j = 0; j < k*2 - 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[len - 1][k*2];//返回最后一天不持有股票的最大利润 dp[len - 1][k*2]。
    }
   
}
  • 时间复杂度:O(k * len),其中len是数组prices的长度,k是允许的交易次数。需要遍历每一天和每一次交易。
  • 空间复杂度:O(k * len),需要一个大小为len×(k*2+1)的二维数组来存储每一天、每一次交易的状态。

一维数组

public class Best_Time_to_Buy_and_Sell_StockIV {
    public int maxProfit3(int k, int[] prices) {
        if(prices.length == 0){
            return 0;
        }
        if(k == 0){
            return 0;
        }
        int[] dp = new int[2 * k];//dp 是一个一维数组,其中 dp[j] 表示第 j 次交易的最大利润。
        for(int i = 0; i < dp.length / 2; i++){
            dp[i * 2] = -prices[0];//初始化 dp[0] 为 -prices[0],表示第0天买入股票。
        }
        for(int i = 1; i <= prices.length; i++){//遍历价格数组,对于每个价格,更新 dp:dp[j]:选择不买入股票或者在今天买入股票,取最大值。dp[j + 1]:选择不卖出股票或者在今天卖出股票,取最大值。
            dp[0] = Math.max(dp[0], -prices[i - 1]);
            dp[1] = Math.max(dp[1], dp[0] + prices[i - 1]);
            for(int j = 2; j < dp.length; j += 2){
                dp[j] = Math.max(dp[j], dp[j - 1] - prices[i-1]);
                dp[j + 1] = Math.max(dp[j + 1], dp[j] + prices[i - 1]);
            }
        }
        return dp[dp.length - 1];//返回最后一天不持有股票的最大利润 dp[dp.length - 1]。
    }
   
}
  • 时间复杂度:O(k * len),其中len是数组prices的长度,k是允许的交易次数。需要遍历每一天和每一次交易。
  • 空间复杂度:O(k),只需要一个大小为2k的一维数组来存储状态,这是因为我们只保留了最后一次买卖的状态。
public class Best_Time_to_Buy_and_Sell_StockIV {
    
    public int maxProfit(int k, int[] prices) {
        if(prices.length == 0 || k == 0)
            return 0;
        int dp[] = new int [k * 2 + 1];//dp 是一个一维数组,其中 dp[j] 表示第 j 次交易的最大利润。初始化 dp[j] 为 -prices[0],表示第0天买入第j次股票。
        for(int i = 1; i < 2 * k + 1; i += 2){
            dp[i] = -prices[0];
        }
        for(int i = 1; i < prices.length; i++){//遍历价格数组,对于每个价格,更新 dp:dp[j]:选择不买入股票或者在今天买入股票,取最大值。dp[j + 1]:选择不卖出股票或者在今天卖出股票,取最大值。
            for(int j = 1; j < 2 * k + 1; j++){
                if(j % 2 == 1)
                    dp[j] = Math.max(dp[j], dp[j - 1] - prices[i]);
                else
                    dp[j] = Math.max(dp[j], dp[j - 1] + prices[i]);
            }
        }
        return dp[2 * k];//返回最后一天不持有股票的最大利润 dp[2 * k]。
    }
}
  • 时间复杂度:O(k * len),其中len是数组prices的长度,k是允许的交易次数。需要遍历每一天和每一次交易。
  • 空间复杂度:O(k),只需要
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值