由于水平原因,博客大部分内容摘抄于网络,如有错误或者侵权请指出,本人将尽快修改

股票问题

121题

给定一个数组prices,其中prices[i]是一支给定股票第i天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。但是,你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例

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

解题思路

贪心法:设置一个买入的价格,每次遇到比买入价格大,就换掉。并且每次都要更新最大收益

    public int maxProfit(int[] prices) {
        int max=0;
        int buy=prices[0];
        for (int i = 1; i < prices.length; i++) {
            max=Math.max(max,prices[i]-buy);
            if (prices[i]<buy){
                buy=prices[i];
            }
        }
        return max;
    }

其他的股票问题均可以使用动态规划解答
动态规划主要是解决两个问题:枚举状态和选择
股票问题的状态:
天数:i
允许交易的最大次数:k
用户账户当前状态(持有或者未持有股票):0,1

dp[i][k][0 or 1] //前面说了三个维度,自然dp数组也是三维的。dp表示利润
for(int i = 0;i < n;i++)
for(int j = 0;j < k;j++)
dp[i][j][0] 取优
dp[i][j][1] 取优//账户状态这个维度只有两种可能,就直接计算就好啦。

122题(k无限制)

给定一个数组prices,其中prices[i]表示某支股票第i天的价格。在每一天,你可以决定是否购买和/或出售股票。你在任何时候最多只能持有一股股票。你也可以先购买,然后在同一天出售。返回你能获得的最大利润。

示例

  • 输入:prices = [7,1,5,3,6,4]
    输出:7
    解释:在第2天(股票价格=1)的时候买入,在第3天(股票价格=5)的时候卖出,这笔交易所能获得利润=5-1=4。随后,在第4天(股票价格=3)的时候买入,在第5天(股票价格=6)的时候卖出,这笔交易所能获得利润=6-3=3。总利润为4+3=7。
  • 输入:prices = [1,2,3,4,5]
    输出:4
    解释:在第1天(股票价格=1)的时候买入,在第5天(股票价格=5)的时候卖出,这笔交易所能获得利润=5-1=4。总利润为4。
  • 输入:prices = [7,6,4,3,1]
    输出:0
    解释:在这种情况下,交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为0。

解题思路

在这个问题中,我们使用动态规划来寻找最优解。我们定义一个二维数组dp,其中dp[i][0]表示第i天交易完后手里没有股票的最大利润,而dp[i][1]表示第i天交易完后手里持有一支股票的最大利润。

根据题目描述,我们可以得到状态转移方程如下:

  1. 如果第i天没有股票(dp[i][0]),那么有两种情况:

    • i-1天也没有股票,并且今天没有进行任何操作(即不买入也不卖出),所以dp[i][0] = dp[i-1][0]
    • i-1天持有股票,但今天卖出了股票,所以dp[i][0] = dp[i-1][1] + prices[i](其中prices[i]是第i天的股票价格)。

    取这两种情况中的较大值,即:dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i])

  2. 如果第i天持有股票(dp[i][1]),那么也有两种情况:

    • i-1天也持有股票,并且今天没有进行任何操作(即不卖出也不买入),所以dp[i][1] = dp[i-1][1]
    • i-1天没有股票,但今天买入了股票,所以dp[i][1] = dp[i-1][0] - prices[i]

    取这两种情况中的较大值,即:dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - prices[i])

注意,初始化时,dp[0][0]应该是0(因为没有股票且没有进行任何交易),而dp[0][1]应该是-prices[0](因为我们在第一天买入了一支股票)。

最后,答案就是dp[n-1][0],其中n是股票价格的长度,表示最后一天交易完后手里没有股票的最大利润。

class Solution {
    public int maxProfit(int[] prices) {
        int[][] dp = new int[prices.length][2];//0为未持有,1为持有
        dp[0][0] = 0;
        dp[0][1] = 0 - prices[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],dp[i - 1][0] - prices[i]);
        }
        return dp[prices.length - 1][0];
    }
}

714题(有费用)

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

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

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

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

此外,你还需要支付交易费用,每次交易(买入或卖出)都需要支付一个固定的费用 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

解题思路

如果第i天不持有股票(dp[i][0]):
第i-1天也不持有股票,今天没有进行任何操作:dp[i][0] = dp[i-1][0]
第i-1天持有股票,今天卖出了股票,需要支付手续费:dp[i][0] = dp[i-1][1] + prices[i] - fee
取这两者中的较大值:
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee);
如果第i天持有股票(dp[i][1]):
第i-1天就持有股票,今天没有进行任何操作:dp[i][1] = dp[i-1][1]
第i-1天不持有股票,但今天买入了股票(注意:这里不直接考虑手续费,因为手续费是在卖出时支付的):dp[i][1] = dp[i-1][0] - prices[i]
取这两者中的较大值:
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);

class Solution {
    public int maxProfit(int[] prices, int fee) {
        int[][] dp = new int[prices.length][2];//0为未持有,1为持有
        dp[0][0] = 0;
        dp[0][1] = 0 - prices[0];
        for(int i = 1;i < prices.length;i++){
            dp[i][0] = Math.max(dp[i - 1][0],dp[i - 1][1] + prices[i] - fee);
            dp[i][1] = Math.max(dp[i - 1][1],dp[i - 1][0] - prices[i]);
        }
        return dp[prices.length - 1][0];
    }
}

309

题目描述

给定一个整数数组prices,其中第i个元素是一支给定股票第i天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

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

示例

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

解题思路

通过定义一个二维数组 dp 来存储不同状态下的最大利润。

变量定义

  1. int n = prices.length;:获取输入股票价格数组的长度。
  2. int[][] dp = new int[n][3];:定义一个二维数组 dp,其中 dp[i][0] 表示第 i 天不持有股票的最大利润,dp[i][1] 表示第 i 天持有股票的最大利润,dp[i][2] 表示第 i 天处于冷冻期的最大利润。

初始化

  1. dp[0][0] = 0;:第一天不持有股票时利润为 0,因为还没有进行任何买卖操作。
  2. dp[0][1] = -prices[0];:第一天持有股票,意味着花费了 prices[0] 的成本,所以利润为 -prices[0]
  3. dp[0][2] = 0;:第一天不可能处于冷冻期,所以利润为 0。

状态转移方程

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

    • i 天不持有股票有两种情况:
      • i - 1 天就不持有股票,利润为 dp[i - 1][0]
      • i - 1 天持有股票,第 i 天卖出,利润为 dp[i - 1][1] + prices[i](卖出价格为 prices[i])。
    • 取这两种情况的较大值作为第 i 天不持有股票的最大利润。
  2. dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][2] - prices[i]);

    • i 天持有股票有两种情况:
      • i - 1 天就持有股票,利润为 dp[i - 1][1]
      • i - 1 天处于冷冻期,第 i 天买入股票,利润为 dp[i - 1][2] - prices[i](买入价格为 prices[i])。
    • 取这两种情况的较大值作为第 i 天持有股票的最大利润。
  3. dp[i][2] = dp[i - 1][0];

    • i 天处于冷冻期,说明第 i - 1 天卖出了股票,所以第 i 天的冷冻期利润等于第 i - 1 天不持有股票的利润,即 dp[i - 1][0]

最终结果

返回 Math.max(dp[n - 1][0], dp[n - 1][2]),因为最后一天的最大利润可能是不持有股票(dp[n - 1][0])或者处于冷冻期(dp[n - 1][2])的情况,取两者中的较大值作为最终的最大利润。

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length <= 1) {
            return 0;
        }
        int n = prices.length;
        // dp[i][0] 表示第 i 天不持有股票的最大利润
        // dp[i][1] 表示第 i 天持有股票的最大利润
        // dp[i][2] 表示第 i 天处于冷冻期的最大利润
        int[][] dp = new int[n][3];
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        dp[0][2] = 0;
        for (int i = 1; i < n; 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], dp[i - 1][2] - prices[i]);
            dp[i][2] = dp[i - 1][0];
        }
        return Math.max(dp[n - 1][0], dp[n - 1][2]);
    }
}

LeetCode 188(最多买卖K次)

题目描述
给定一个数组,它的第i个元素是一支给定的股票在第i天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成k笔交易。注意,你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例

  • 输入:prices = [2, 4, 1], k = 2
    输出:2
    解释:在第1天(股票价格=2)的时候买入,在第2天(股票价格=4)的时候卖出,这笔交易所能获得利润=4-2=2。

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

解题思路

枚举框架解释

需要对三个维度进行枚举:

  1. “天”:即遍历每一天,观察在不同天数下的股票状态和利润情况。
  2. “最大交易次数”:考虑不同的交易次数限制对最终利润的影响。
  3. “账户状态”:分为持有股票和未持有股票两种状态。

状态转移方程解释

  1. 当天未持有股票的情况:

    • 第一种情况是昨天就未持有,今天也不买,此时利润与昨天未持有股票时的利润相同,即 dp[i - 1][j][0]
    • 第二种情况是昨天持有,今天卖出。卖出操作不会增加交易次数,因为一次完整的买入和卖出算作一次交易,所以是在当前交易次数维度进行转移,利润为昨天持有股票时的利润加上今天卖出的价格,即 dp[i - 1][j][1] + prices[i]
    • 最终当天未持有股票的最大利润是这两种情况中的较大值,即 dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i])
  2. 当天持有股票的情况:

    • 第一种情况是昨天持有,今天继续持有,利润与昨天持有股票时的利润相同,即 dp[i - 1][j][1]
    • 第二种情况是昨天未持有,今天买入。注意买入操作会引起交易次数的变化,因为买入算作一次交易的开始,所以要从昨天未持有且交易次数少一次的状态转移过来,利润为昨天未持有且交易次数为 j - 1 时的利润减去今天买入的价格,即 dp[i - 1][j - 1][0] - prices[i]
    • 最终当天持有股票的最大利润是这两种情况中的较大值,即 dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i])
class Solution {
    public int maxProfit(int k, int[] prices) {
        if (prices == null || prices.length < 2) {
            return 0;
        }
        // dp[i][j][0]表示第i天、进行了j次交易且当前未持有股票时的最大利润
        // dp[i][j][1]表示第i天、进行了j次交易且当前持有股票时的最大利润
        int[][][] dp = new int[prices.length][k + 1][2];

        // 初始化第一天的情况
        for (int i = 0; i <= k; i++) {
            // 第一天无论进行多少次交易,当持有股票时,利润为0 - prices[0],即花费prices[0]的价格买入股票
            dp[0][i][1] = 0 - prices[0];
        }

        // 从第二天开始计算每一天的状态
        for (int i = 1; i < prices.length; i++) {
            for (int j = 1; j <= k; j++) {
                // 第i天未持有股票的最大利润
                // 情况一:前一天就未持有股票,即dp[i - 1][j][0]
                // 情况二:前一天持有股票,今天卖出,利润为dp[i - 1][j][1] + prices[i]
                dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);

                // 第i天持有股票的最大利润
                // 情况一:前一天就持有股票,即dp[i - 1][j][1]
                // 情况二:前一天未持有股票,今天买入,并且由于买入操作会使交易次数减少一次,
                // 所以是从dp[i - 1][j - 1][0]的状态转移过来,利润为dp[i - 1][j - 1][0] - prices[i]
                dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
            }
        }

        // 返回最后一天,进行了k次交易且未持有股票时的最大利润
        return dp[prices.length - 1][k][0];
    }
}

LeetCode 188(最多买卖2次)

解题思路
按照188题,只需要把k改成2,就能解决。

参考文献

[1] https://www.cnblogs.com/wunsiang/p/12771442.html

posted @ 2024-09-08 10:25  小纸条  阅读(56)  评论(0)    收藏  举报