股票市场交易|豆包MarCodeAI刷题

116 阅读4分钟

问题描述

小R近期表现出色,公司决定以股票的形式给予奖励,并允许他在市场上进行交易以最大化收益。给定一个数组,数组中的第 i 个元素代表第 i 天的股票价格。小R需要设计一个算法来实现最大利润。 股票交易规则如下: • 小R可以多次买卖股票,但在买入新的股票前必须卖出之前的股票。 • 每次卖出股票后存在一天的冷冻期,在冷冻期内小R不能购买股票。 你的任务是帮助小R计算出在遵守交易规则的情况下能够获得的最大利润。 • stocks: 一个整数列表,表示连续几天内的股票价格。

问题理解

详细解释

初始检查: 这段代码检查输入的 stocks 数组是否为空或长度为零。如果是,直接返回 0,因为没有任何股票可以交易。

    if (stocks == null || stocks.length == 0) {
    return 0;
}

定义状态数组: - ``hold[i]:表示第 i 天持有股票时的最大利润。 - unhold[i]:表示第 i` 天不持有股票时的最大利润。

int n = stocks.length;
int[] hold = new int[n];
int[] unhold = new int[n];

初始化状态

for (int i = 1; i < n; i++) {
    hold[i] = Math.max(hold[i-1], (i >= 2 ? unhold[i-2] : 0) - stocks[i]);
    unhold[i] = Math.max(unhold[i-1], hold[i-1] + stocks[i]);
}
  • hold[0] = -stocks[0]:第一天买入股票,利润为负的股票价格。
  • unhold[0] = 0:第一天不持有股票,利润为 0

状态转移方程

  • hold[i] = Math.max(hold[i-1], (i >= 2 ? unhold[i-2] : 0) - stocks[i])

  • hold[i-1]:表示前一天持有股票的最大利润。

  • (i >= 2 ? unhold[i-2] : 0) - stocks[i]:表示前两天不持有股票的最大利润减去当天的股票价格(因为存在冷冻期)。

  • unhold[i] = Math.max(unhold[i-1], hold[i-1] + stocks[i])

  • unhold[i-1]:表示前一天不持有股票的最大利润。

  • hold[i-1] + stocks[i]:表示前一天持有股票的最大利润加上当天的股票价格。

for (int i = 1; i < n; i++) {
   hold[i] = Math.max(hold[i-1], (i >= 2 ? unhold[i-2] : 0) - stocks[i]);
   unhold[i] = Math.max(unhold[i-1], hold[i-1] + stocks[i]);
}
  1. 返回结果
return unhold[n-1];
最终结果是不持有股票的最大利润,即 `unhold[n-1]`。

总结

  • 状态定义hold[i] 和 unhold[i] 分别表示第 i 天持有和不持有股票的最大利润。
  • 状态转移:根据前一天的状态和当天的股票价格来更新状态。
  • 初始化:第一天持有股票的利润为负的股票价格,不持有股票的利润为 0
  • 最终结果:返回最后一天不持有股票的最大利润。

动态规划

动态规划(Dynamic Programming,简称DP)是一种通过将问题分解为更小的子问题来解决复杂问题的算法技术。它通常用于优化问题,其中问题的解决方案可以通过组合子问题的解决方案来获得。

动态规划的核心思想

  1. 最优子结构:问题的最优解可以通过其子问题的最优解来构造。这意味着问题的解可以分解为多个子问题的解。
  2. 重叠子问题:在递归求解过程中,许多子问题是重复的。动态规划通过存储这些子问题的解来避免重复计算,从而提高效率。

动态规划的基本步骤

  1. 定义状态:确定问题的状态表示。状态通常是一个或多个变量的组合,表示问题的某个特定情况。
  2. 状态转移方程:找到状态之间的关系,即如何从一个状态转移到另一个状态。这通常通过递推关系式来表示。
  3. 初始条件:确定初始状态的值,这些值是递推的基础。
  4. 计算顺序:确定计算状态的顺序,通常是从初始状态开始,逐步计算到最终状态。
  5. 返回结果:根据问题的要求,返回最终状态的值。

动态规划的常见应用

  • 斐波那契数列:通过存储中间结果来避免重复计算。
  • 最长公共子序列(LCS) :找到两个序列的最长公共子序列。
  • 背包问题:在给定重量限制下,选择物品以最大化价值。
  • 最短路径问题:在图中找到从起点到终点的最短路径

总代码

public class Main {
    public static int solution(int[] stocks) {
        if (stocks == null || stocks.length == 0) {
            return 0;
        }
        
        int n = stocks.length;
        int[] hold = new int[n];
        int[] unhold = new int[n];
        
        // 初始化
        hold[0] = -stocks[0];
        unhold[0] = 0;
        
        for (int i = 1; i < n; i++) {
            // 状态转移方程
            hold[i] = Math.max(hold[i-1], (i >= 2 ? unhold[i-2] : 0) - stocks[i]);
            unhold[i] = Math.max(unhold[i-1], hold[i-1] + stocks[i]);
        }
        
        // 最终结果是不持有股票的最大利润
        return unhold[n-1];
    }

    public static void main(String[] args) {
        // You can add more test cases here
        System.out.println(solution(new int[]{1, 2}) == 1);
        System.out.println(solution(new int[]{2, 1}) == 0);
        System.out.println(solution(new int[]{1, 2, 3, 0, 2}) == 3);
        System.out.println(solution(new int[]{2, 3, 4, 5, 6, 7}) == 5);
        System.out.println(solution(new int[]{1, 6, 2, 7, 13, 2, 8}) == 12);
    }
}