Bootstrap

⭐算法OJ⭐最佳买卖股票时机(含“冷却期”或“交易费”)【动态规划 + 滚动窗口】(C++实现)Best Time to Buy and Sell Stock 系列

这一篇给大家分享两篇股票买卖系列的扩展题目,难度不太大,如果对这类问题不是很熟悉可以参考⭐算法OJ⭐最佳买卖股票时机【贪心算法 + 动态规划】(C++实现)Best Time to Buy and Sell Stock 系列 I,II,III,IV

309. Best Time to Buy and Sell Stock with Cooldown

You are given an array prices where prices[i] is the price of a given stock on the i t h i^{th} ith day.

Find the maximum profit you can achieve. You may complete as many transactions as you like (i.e., buy one and sell one share of the stock multiple times) with the following restrictions:

  • After you sell your stock, you cannot buy stock on the next day (i.e., cooldown one day).

Note: You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again).

Example 1:

Input: prices = [1,2,3,0,2]
Output: 3
Explanation: transactions = [buy, sell, cooldown, buy, sell]

Example 2:

Input: prices = [1]
Output: 0

问题描述

给定一个数组 prices,其中 prices[i] 表示第 i 天股票的价格。你需要找到可以获得的最大利润。交易规则如下:

  • 你可以进行多次交易(即多次买入和卖出)。
  • 每次卖出股票后,必须等待一天(冷却期)才能再次买入。
  • 不能同时参与多笔交易(即必须在再次买入之前卖出当前的股票)。

动态规划解法

这个问题是一个经典的股票买卖问题,带有“冷却期”限制。我们可以使用动态规划来解决。用动态规划来记录每一天的状态,并更新最大利润。定义以下状态:

  • dp[i][0]:第 i 天结束时,持有股票的最大利润。
  • dp[i][1]:第 i 天结束时,不持有股票且处于冷却期的最大利润。
  • dp[i][2]:第 i 天结束时,不持有股票且不处于冷却期的最大利润。

C++ 代码实现

#include <vector>
#include <algorithm>
using namespace std;

int maxProfit(vector<int>& prices) {
   if (prices.empty()) {
       return 0;
   }

   int n = prices.size();
   // 定义动态规划数组
   vector<vector<int>> dp(n, vector<int>(3, 0));

   // 初始条件
   dp[0][0] = -prices[0]; // 第 0 天持有股票
   dp[0][1] = 0;          // 第 0 天不持有股票且处于冷却期
   dp[0][2] = 0;          // 第 0 天不持有股票且不处于冷却期

   for (int i = 1; i < n; i++) {
       // 状态转移方程
       dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i]); // 持有股票
       dp[i][1] = dp[i-1][0] + prices[i];                  // 不持有股票且处于冷却期
       dp[i][2] = max(dp[i-1][2], dp[i-1][1]);             // 不持有股票且不处于冷却期
   }

   // 最终结果是最后一天不持有股票的状态中的最大值
   return max(dp[n-1][1], dp[n-1][2]);
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n nprices 的长度。
  • 空间复杂度: O ( n ) O(n) O(n),可以用滚动数组优化到 O ( 1 ) O(1) O(1)

空间优化版本

为了进一步优化空间复杂度,我们可以使用滚动数组,只保留前一天的状态。这个优化方式可以类比⭐算法OJ⭐跳跃游戏【BFS+滑动窗口】(C++实现)Jump Game 系列 III,VII

int maxProfit(vector<int>& prices) {
    if (prices.empty()) {
        return 0;
    }

    int n = prices.size();
    int dp0 = -prices[0]; // 持有股票
    int dp1 = 0;          // 不持有股票且处于冷却期
    int dp2 = 0;          // 不持有股票且不处于冷却期

    for (int i = 1; i < n; i++) {
        int new_dp0 = max(dp0, dp2 - prices[i]); // 持有股票
        int new_dp1 = dp0 + prices[i];           // 不持有股票且处于冷却期
        int new_dp2 = max(dp2, dp1);             // 不持有股票且不处于冷却期

        dp0 = new_dp0;
        dp1 = new_dp1;
        dp2 = new_dp2;
    }

    return max(dp1, dp2);
}

空间复杂度为 O ( 1 ) O(1) O(1),更加高效。

714. Best Time to Buy and Sell Stock with Transaction Fee

You are given an array prices where prices[i] is the price of a given stock on the i t h i^{th} ith day, and an integer fee representing a transaction fee.

Find the maximum profit you can achieve. You may complete as many transactions as you like, but you need to pay the transaction fee for each transaction.

Note:

  • You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again).
  • The transaction fee is only charged once for each stock purchase and sale.

问题描述

给定一个数组 prices,其中 prices[i] 表示第 i 天股票的价格,以及一个整数 fee 表示每次交易的手续费。你需要找到可以获得的最大利润。交易规则如下:

  • 你可以进行多次交易(即多次买入和卖出)。
  • 每次交易需要支付手续费 fee
  • 不能同时参与多笔交易(即必须在再次买入之前卖出当前的股票)。

动态规划解法

这个问题是带有交易费用的股票买卖问题。我们可以使用动态规划记录每一天的状态,并更新最大利润。定义以下状态:

  • dp[i][0]:第 i 天结束时,持有股票的最大利润。
  • dp[i][1]:第 i 天结束时,不持有股票的最大利润。

状态转移方程

  1. dp[i][0](持有股票):
    • 可能是前一天就持有股票,今天什么也不做:dp[i-1][0]
    • 也可能是前一天不持有股票,今天买入股票:dp[i-1][1] - prices[i]
    • 取两者中的最大值:dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i])
  2. dp[i][1](不持有股票):
    • 可能是前一天就不持有股票,今天什么也不做:dp[i-1][1]
    • 也可能是前一天持有股票,今天卖出股票并支付手续费:dp[i-1][0] + prices[i] - fee
    • 取两者中的最大值:dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i] - fee)

初始条件

  • 第 0 天结束时:
    • 如果持有股票,则利润为 -prices[0]:dp[0][0] = -prices[0]。
    • 如果不持有股票,则利润为 0:dp[0][1] = 0。

最终结果

最大利润为最后一天不持有股票的状态:dp[n-1][1]

C++ 代码实现

#include <vector>
#include <algorithm>
using namespace std;

int maxProfit(vector<int>& prices, int fee) {
    if (prices.empty()) {
        return 0;
    }

    int n = prices.size();
    // 定义动态规划数组
    vector<vector<int>> dp(n, vector<int>(2, 0));

    // 初始条件
    dp[0][0] = -prices[0]; // 第 0 天持有股票
    dp[0][1] = 0;          // 第 0 天不持有股票

    for (int i = 1; i < n; i++) {
        // 状态转移方程
        dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i]); // 持有股票
        dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i] - fee); // 不持有股票
    }

    // 最终结果是最后一天不持有股票的状态
    return dp[n-1][1];
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n nprices 的长度。
  • 空间复杂度: O ( n ) O(n) O(n),可以用滚动数组优化到 O ( 1 ) O(1) O(1)

空间优化版本

为了进一步优化空间复杂度,我们可以使用滚动数组,只保留前一天的状态:

int maxProfit(vector<int>& prices, int fee) {
    if (prices.empty()) {
        return 0;
    }

    int n = prices.size();
    int dp0 = -prices[0]; // 持有股票
    int dp1 = 0;          // 不持有股票

    for (int i = 1; i < n; i++) {
        int new_dp0 = max(dp0, dp1 - prices[i]); // 持有股票
        int new_dp1 = max(dp1, dp0 + prices[i] - fee); // 不持有股票

        dp0 = new_dp0;
        dp1 = new_dp1;
    }

    return dp1;
}

这个版本的空间复杂度 O ( 1 ) O(1) O(1),更加高效。

;