• 几种股票交易问题


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

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

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

    ​ 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

    示例:

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

    解析

    ​ 此处的交易次数是无限制的,含有冷冻期,即卖出的第二天是无法买入股票的。设置一个状态转移数组dp:

    dp[i][0]  截止到第i天,此时没有拥有一支股票,所拥有的最大收益
    dp[i][1]  截止到第i天,此时拥有一支股票,所拥有的最大收益
    

    ​ 那么状态转移数组就是:

    dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] + prices[i-1]);
    dp[i][1] = Math.max(dp[i-1][1],dp[i-2][0] - prices[i-1]);
    

    ​ 此处求dp[i][1]时,从dp[i-2][0]处进行状态转移,是因为交易包含一个冷冻期,如果是今天购买一支股票,那么必须肯定是在前天没股票的时候基础上,选择在今天买入。

    (此处可能会有一个疑问,那么就是第i天的前一天也有没有拥有股票的情况,并且第i-1天也没有卖出股票,那么为什么不能从i-1天状态转移呢?从我们的第i天没有拥有股票的最大收益的状态转移表达式可知,第i天没有拥有股票这件事实造成的原因有两种:

    ​ 1、第i-1天的前一天,即第i-2天就没有拥有股票,所以第i-1天没有拥有股票,那么此时,第i-1天的没有拥有股票的状态就是从第i-2天没有拥有股票的状态转移过来的,所以即使在计算第i天拥有股票的最大收益时选择从第i-1天的状态转移,那实质上还是从第i-2天转移的;

    ​ 2、第i-1天发生了股票的卖出操作,那么第i天应该是一个冷冻期,不能进行买入操作;

    所以综上两点,第i天发生买入操作,应该是从第i-2天转移的。

    public int maxProfit(int[] prices) {
    
        if (prices == null || prices.length == 0){
            return 0;
        }
        int n = prices.length;
    
        int[][] dp = new int[n+1][2];
    
        dp[1][0] = 0;
        dp[1][1] = -prices[0];
    
        for (int i = 2; i <= n; i++) {
    
            dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] + prices[i-1]);
            dp[i][1] = Math.max(dp[i-1][1],dp[i-2][0] - prices[i-1]);
        }
    
        return dp[n][0];
    }
    

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

    ​ 给定一个整数数组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.
    

    多了一个交易费,那就在每次卖出的时候,减去fee即可

    public int maxProfit(int[] prices, int fee) {
    
        if (prices == null || prices.length == 0){
            return 0;
        }
    
        int n = prices.length;
        int[][] dp = new int[n+1][2];
    
        dp[1][0] = 0;
        dp[1][1] = -prices[0];
    
        for (int i = 2; i <= n; i++) {
    
            dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i-1]-fee);
            dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]-prices[i-1]);
        }
    
        return dp[n][0];
    }
    

    优化一下空间:

    public int maxProfit2(int[] prices, int fee) {
    
        if (prices == null || prices.length == 0){
            return 0;
        }
    
        int n = prices.length;
        int dp_i_0 = 0;
        int dp_i_1 = -prices[0];
    
        for (int i = 2; i <= n; i++) {
    
            int temp = dp_i_0;
            dp_i_0 = Math.max(dp_i_0,dp_i_1+prices[i-1]-fee);
            dp_i_1 = Math.max(dp_i_1,temp-prices[i-1]);
        }
        return dp_i_0;
    }
    

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

    ​ 给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

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

    示例 1:

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

    示例 2:

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

    ​ 因为最多只能进行两次交易,所以必须要考虑交易次数这个状态了,所以状态数组为:

    ​ dp[i][k][j]表示,截止到第i天,发生的第k次交易(前提是第k-1次交易必须完成),手上目前拥有了j个股票时的最大收益

    public int maxProfit(int[] prices) {
    
        if (prices.length <= 1){
            return 0;
        }
    
        int n = prices.length;
    
        //dp[i][k][j]表示,截止到第i天,发生的第k次交易(前提是第k-1次交易必须完成),手上目前拥有了j个股票时的最大收益
        int[][][] dp = new int[n+1][3][2];
    
        dp[1][1][0] = 0;
        dp[1][1][1] = -prices[0];
        dp[1][2][0] = 0;
        //此处我们可以认为,在第一天已经发生了一次交易,即买入卖出,那么当天买入又卖出,那就是收益为0
        //所以发生第二次交易的买入时,此时收益就是-prices[0];
        dp[1][2][1] = -prices[0];
        for (int i = 2; i <= n; i++) {
            dp[i][1][0] = Math.max(dp[i-1][1][0],dp[i-1][1][1] + prices[i-1]);
            dp[i][1][1] = Math.max(dp[i-1][1][1],-prices[i-1]);
            dp[i][2][0] = Math.max(dp[i-1][2][0],dp[i-1][2][1] + prices[i-1]);
            dp[i][2][1] = Math.max(dp[i-1][2][1],dp[i-1][1][0] - prices[i-1]);
        }
    
        return dp[n][2][0];
    }
    

    ​ dp[i][1][1] 表示在前i天,发生第一次交易,此时拥有一支股票的最大收益,该值要么是第i-1天第一次交易时已经拥有了,要么就是在第i天当天买入的,那么当天买入时,由于这是第一次交易,所以当前的利润就是-prices[i-1]

    ​ 发生第二次交易的前提是:第一次交易必须完成,所以第二次交易的买入操作,应该是从第一次交易时没有拥有股票的状态转移过来的,即dp[i-1][1][0] - prices[i-1]

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

    给定一个整数数组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 。
    

    解析

    ​ 此处的交易次数k为一个随机数,所以无法直接列出k的所有情况,如果直接两层循环,则会发生超时的问题,从而无法正常提交。通过分析我们可以知道,因为不能同时开展多次交易,而一次完整的交易会使用2天的时间,所以,如果 k>n/2的话,那么可以认为是无限制交易次数的;如果k <= n/2,则需要考虑交易次数这个状态。所以本题的解决方法就是将二者结合起来。

    public int maxProfit(int k, int[] prices) {
    
        if (k == 0 || prices.length == 0){
            return 0;
        }
    
        int n = prices.length;
    
        if (k > n/2){
    
            int[][] dp = new int[n+1][2];
            dp[1][0] = 0;
            dp[1][1] = -prices[0];
    
            for (int i = 2; i <= n; i++) {
    
                dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] + prices[i-1]);
                dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0] - prices[i-1]);
            }
    
            return dp[n][0];
        }
    
        int[][][] dp = new int[n+1][k+1][2];
    
        for (int i = 1; i <= k; i++) {
            dp[1][i][0] = 0;
            dp[1][i][1] = -prices[0];
        }
    
        for (int i = 2; i <= n; i++) {
    
            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-1]);
                dp[i][j][1] = Math.max(dp[i-1][j][1],dp[i-1][j-1][0] - prices[i-1]);
            }
        }
        return dp[n][k][0];
    }
    
  • 相关阅读:
    LeetCode 109 Convert Sorted List to Binary Search Tree
    LeetCode 108 Convert Sorted Array to Binary Search Tree
    LeetCode 107. Binary Tree Level Order Traversal II
    LeetCode 106. Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal
    LeetCode 103 Binary Tree Zigzag Level Order Traversal
    LeetCode 102. Binary Tree Level Order Traversal
    LeetCode 104. Maximum Depth of Binary Tree
    接口和多态性
    C# 编码规范
  • 原文地址:https://www.cnblogs.com/yxym2016/p/14451275.html
Copyright © 2020-2023  润新知