学习方法与心得5 | 豆包MarsCode AI 刷题-动态规划专题2

395 阅读5分钟

股票交易问题

1. 简单的股票交易问题(不含冷冻期)

1.1 问题描述

考虑一个数组 prices,其中第 i 个元素 prices[i] 表示某支股票在第 i 天的市场价格。

你需要在某一天购买这支股票,并在未来的某一天(不同于购买日)将其出售。请设计一个算法,以计算你能够获得的最大利润。

最终,返回你通过这笔交易所能实现的最大利润。如果无法获得任何利润,则返回 0。

1.2 解题思路

给定一个数组 prices,其中每个元素代表某一天的股票价格。我们的目标是选择一个买入日和一个卖出日,确保买入日早于卖出日,并计算出最大利润。关键在于:

  1. 买入价格最低:在遍历价格数组时,我们需要记录当前的最低买入价格。
  2. 计算利润:在每一天,我们可以计算当前价格与最低买入价格的差值,这个差值就是在当前价格下的潜在利润。
  3. 更新最大利润:每次计算出的潜在利润如果超过当前的最大利润,则更新最大利润。
图解

可以通过一张图来理解这个过程:

价格:   | 3 | 2 | 6 | 5 | 0 | 3 |  
         -------------------------  
天数:   0   1   2   3   4   5
  • 初始状态:设定 minPrice 为一个很大的数,maxProfit 为 0。

  • 遍历过程

    • 第一天价格 3,更新 minPrice 为 3。
    • 第二天价格 2,更新 minPrice 为 2。
    • 第三天价格 6,计算利润 6 - 2 = 4,更新 maxProfit 为 4。
    • 第四天价格 5,计算利润 5 - 2 = 3maxProfit 保持不变。
    • 第五天价格 0,更新 minPrice 为 0。
    • 第六天价格 3,计算利润 3 - 0 = 3maxProfit 保持不变。

最终,最大利润为 4。

1.3 代码详解

下面是代码的详细解析:

int maxProfit(vector<int>& prices) {  
    int minpirce = 10010; // 初始化最低价格为一个较大的数  
    int maxans = 0; // 初始化最大利润为 0  
        
    for(int i = 0; i < prices.size(); i++) {  
        minpirce = min(minpirce, prices[i]); // 更新最低价格  
        maxans = max(prices[i] - minpirce, maxans); // 更新最大利润  
    }  
        
    return maxans; // 返回最大利润  
}  
  • 初始化minpirce 被初始化为一个较大的数,以确保任何股票价格都会低于这个值。maxans 初始化为 0,表示一开始没有利润。

  • 循环遍历:使用一个 for 循环遍历 prices 数组:

    • minpirce = min(minpirce, prices[i]):更新当前的最低买入价格。
    • maxans = max(prices[i] - minpirce, maxans):计算当前价格与最低买入价格的差,并更新最大利润。
  • 返回结果:最后返回 maxans,即最大利润。

2. 复杂的股票交易问题(含冷冻期)

2.1 问题描述

小R近期表现出色,公司决定以股票的形式给予奖励,并允许他在市场上进行交易以实现最大化收益。给定一个数组 stocks,数组中的第 i 个元素代表第 i 天的股票价格。小R需要设计一个算法来计算他在遵循以下交易规则的情况下能够获得的最大利润:

  1. 小R可以多次买入和卖出股票,但在每次卖出后必须等待一天的冷冻期,期间无法再买入股票。
  2. 在开始新的交易之前,小R必须先卖出持有的股票。

2.2 解题思路

给定一个数组 prices,其中每个元素表示某一天的股票价格。我们的目标是选择一个买入日和一个卖出日,以获取最大利润。在这个问题中,我们需要考虑以下几点:

  1. 状态定义

    • f[i][0]: 表示第 i 天持有股票的最大利润。
    • f[i][1]: 表示第 i 天卖出股票的最大利润。
    • f[i][2]: 表示第 i 天不持有股票且没有进行交易的最大利润。
  2. 状态转移方程

    • f[i][0]:可以从前一天持有股票(f[i-1][0])或在前一天不持有股票并在今天买入(f[i-1][2] - stocks[i])。
    • f[i][1]:表示在第 i 天卖出股票,可以由前一天持有股票(f[i-1][0] + stocks[i])得到。
    • f[i][2]:表示在第 i 天不持有股票,可以由前一天卖出股票(f[i-1][1])或不持有股票(f[i-1][2])得到。
  3. 结果:最后的最大利润为 max(f[stocksSize-1][1], f[stocksSize-1][2]),即在最后一天卖出股票的利润或不持有股票的利润。

图解

可以通过一张图来理解这个过程:

价格:   | 1 | 2 | 3 | 0 | 2 |  
         -------------------------  
天数:   0   1   2   3   4
  • 状态变化

    • 第一天:买入股票,状态为 f[0][0] = -1;不持有股票,状态为 f[0][1] = 0 和 f[0][2] = 0
    • 第二天:卖出股票,状态为 f[1][1] = 1;持有股票,状态为 f[1][0] = -1;不持有股票,状态为 f[1][2] = 0
    • 第三天:继续分析,更新状态,最终计算出最大利润。

1.3 代码详解

下面是代码的详细解析:

#include <iostream>  
#include <vector>  
#include <algorithm>  

int solution(std::vector<int> stocks) {  
    int stocksSize = stocks.size();  
    if (stocksSize == 0)  
        return 0;  

    int f[stocksSize][3]; // 状态数组  
    f[0][0] = -stocks[0]; // 第一天持有股票的利润  
    f[0][1] = f[0][2] = 0; // 第一天不持有股票的利润  

    for (int i = 1; i < stocksSize; i++) {  
        f[i][0] = std::max(f[i-1][0], f[i-1][2] - stocks[i]); // 持有股票的最大利润  
        f[i][1] = f[i-1][0] + stocks[i]; // 卖出股票的利润  
        f[i][2] = std::max(f[i-1][1], f[i-1][2]); // 不持有股票的利润  
    }  

    return std::max(f[stocksSize-1][1], f[stocksSize-1][2]); // 返回最大利润  
}  

int main() {  
    std::cout << (solution({1, 2}) == 1) << std::endl;  
    std::cout << (solution({2, 1}) == 0) << std::endl;  
    std::cout << (solution({1, 2, 3, 0, 2}) == 3) << std::endl;  
    std::cout << (solution({2, 3, 4, 5, 6, 7}) == 5) << std::endl;  
    std::cout << (solution({1, 6, 2, 7, 13, 2, 8}) == 12) << std::endl;  
    return 0;  
}
  • 初始化:首先检查股票数组的大小,如果为 0,直接返回 0。然后初始化状态数组 f,第一天的状态设置为持有股票的负利润。

  • 循环遍历:使用一个 for 循环遍历 stocks 数组,更新每一天的状态。

    • f[i][0]:更新持有股票的最大利润。
    • f[i][1]:计算在第 i 天卖出股票后的利润。
    • f[i][2]:更新不持有股票的利润。
  • 返回结果:最后返回在最后一天卖出股票或不持有股票的最大利润。