股票交易的艺术:动态规划带你玩转股市

引言:炒股不易,DP来助!

"股市有风险,入市需谨慎!" 这句话想必大家耳熟能详。但今天,我们不聊那些高深莫测的金融知识,也不预测明天是涨是跌,我们来聊点更"实在"的------如何用计算机科学的"魔法"------动态规划,来模拟股票交易,并从中获取最大利润!

你可能会想,这跟炒股有啥关系?别急,虽然我们不能真的靠算法在股市里一夜暴富(那可是违法的!),但这些股票买卖问题却是算法面试中的"常客",也是理解动态规划思想的绝佳案例。它们就像一个个精心设计的"小游戏",让你在解决问题的过程中,体会到DP的精妙之处。本文将带你领略六道经典的LeetCode股票DP问题,从简单到复杂,层层递进。我们将一起揭开它们的面纱,深入探讨其背后的动态规划思想,并为你提供清晰的解题思路和代码实现。更棒的是,我们还会为你准备一份"武功秘籍"------对比表格,让你一眼看穿这些问题的异同。🚀

动态规划基础:DP是个啥?

在正式"炒股"之前,我们得先搞清楚我们的"交易工具"------动态规划。动态规划,听起来是不是有点高大上?其实,它就是一种解决复杂问题的"分而治之"策略,只不过它更强调"记忆化"和"最优子结构"。

简单来说,当一个问题可以被分解成若干个相互重叠的子问题,并且通过解决这些子问题,最终能够得到原问题的最优解时,动态规划就派上用场了。它会把每个子问题的解都"记住",下次再遇到相同的子问题时,就直接从"记忆库"里取出来,避免重复计算,大大提高效率。这就像你玩游戏打怪升级,每次打完一个怪,都会把它的经验值和掉落物品记录下来,下次再遇到同款怪,你就知道怎么打最省力了。是不是很形象?😂

在股票买卖问题中,我们通常会定义dp数组来表示在某个时间点、某种状态下的最大利润。例如,dp[i][0]可能表示第i天"持有股票"时的最大利润,而dp[i][1]则表示第i天"不持有股票"时的最大利润。通过不断地"推导"和"更新"这些状态,我们最终就能找到在整个交易周期内的最大利润。是不是有点小期待了?那就让我们赶紧进入实战环节吧!

经典股票问题逐个击破

1. 买卖股票的最佳时机 (LeetCode 121)

问题描述:

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天买入这只股票,并在未来的某一天卖出 这只股票。设计一个算法来计算你所能获取的最大利润。注意:你不能在买入前卖出股票。比如,你不能在第二天买入,第一天卖出。这不符合逻辑嘛!🤔

动态规划思路

这道题是股票系列中最简单的一道,因为它只允许我们进行 一次交易。我们可以用动态规划来解决,但其实用贪心算法会更直观、更高效。

我们可以定义 dp[i][0] 表示第 i 天持有股票的最大利润,dp[i][1] 表示第 i 天不持有股票的最大利润。

  • dp[i][0]

    • 如果第 i 天选择持有股票,那么可能是前一天就持有(dp[i-1][0]),或者今天买入(-prices[i])。由于只能买入一次,所以今天买入的利润就是 -prices[i]
    • 因此,dp[i][0] = Math.max(dp[i-1][0], -prices[i])
  • dp[i][1]

    • 如果第 i 天选择不持有股票,那么可能是前一天就不持有(dp[i-1][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] = -prices[0] (第一天买入股票),dp[0][1] = 0 (第一天不持有股票)。 最终结果就是 dp[len-1][1]

贪心思路(更优解)

既然只能进行一次交易,我们只需要找到历史最低点买入,然后在最高点卖出即可。但这个最高点必须在最低点之后。所以,我们可以维护一个变量 minPrice 记录当前遍历到的最低价格,再维护一个变量 maxProfit 记录当前的最大利润。遍历数组,每次更新 minPrice,然后用当前价格减去 minPrice 来更新 maxProfit。这就像你在菜市场买菜,总是想在菜价最低的时候买入,然后在菜价最高的时候卖出,赚个差价。💰

代码示例

js 复制代码
 // 动态规划解法
 var maxProfit_dp = function(prices) {
     const len = prices.length;
     if (len <= 1) return 0;
 ​
     let dp = new Array(len).fill(0).map(() => new Array(2).fill(0));
 ​
     dp[0][0] = -prices[0]; // 第0天持有股票
     dp[0][1] = 0;          // 第0天不持有股票
 ​
     for (let i = 1; i < len; i++) {
         dp[i][0] = Math.max(dp[i - 1][0], -prices[i]);
         dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
     }
 ​
     return dp[len - 1][1];
 };
 ​
 // 贪心解法
 var maxProfit_greedy = function(prices) {
     let minPrice = Infinity; // 记录历史最低价格
     let maxProfit = 0;       // 记录最大利润
 ​
     for (let i = 0; i < prices.length; i++) {
         minPrice = Math.min(minPrice, prices[i]); // 更新最低价格
         maxProfit = Math.max(maxProfit, prices[i] - minPrice); // 更新最大利润
     }
     return maxProfit;
 };

2. 买卖股票的最佳时机 II (LeetCode 122)

问题描述:

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你可以尽可能多地完成交易(多次买卖一支股票)。注意:你不能同时参与多笔交易(即,在再次购买前,你必须卖出之前的股票)。比如,你今天买了,明天卖了,后天又可以买。这就像是"短线操作",只要有钱赚,就赶紧出手!🚀

动态规划思路

这道题与上一道最大的不同在于,我们可以进行 多次交易。这听起来好像更复杂了,但实际上,它反而变得更简单了!

我们仍然可以使用动态规划。定义 dp[i][0] 表示第 i 天持有股票的最大利润,dp[i][1] 表示第 i 天不持有股票的最大利润。

  • dp[i][0]

    • 如果第 i 天选择持有股票,那么可能是前一天就持有(dp[i-1][0]),或者今天买入(dp[i-1][1] - prices[i])。注意,这里买入的利润是 dp[i-1][1] - prices[i],因为我们可以从前一天不持有股票的状态买入。
    • 因此,dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] - prices[i])
  • dp[i][1]

    • 如果第 i 天选择不持有股票,那么可能是前一天就不持有(dp[i-1][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] = -prices[0]dp[0][1] = 0。 最终结果是 dp[len-1][1]

贪心思路(更优解)

由于可以进行多次交易,我们只需要抓住每一次上涨的机会。如果今天的价格比昨天的价格高,那么我们就在昨天买入,今天卖出,赚取这部分差价。这就像是"积少成多",每次赚一点,最终也能积累不少利润。这种策略之所以可行,是因为多次交易的利润可以分解为每次独立交易的利润之和。例如,[1, 2, 3, 4],利润是 (2-1) + (3-2) + (4-3) = 3,这等价于 4-1 = 3。所以,只要有利润,就大胆地赚!🤑

代码示例

js 复制代码
 // 动态规划解法
 var maxProfit_dp_ii = function(prices) {
     const len = prices.length;
     if (len <= 1) return 0;
 ​
     let dp = new Array(len).fill(0).map(() => new Array(2).fill(0));
 ​
     dp[0][0] = -prices[0];
     dp[0][1] = 0;
 ​
     for (let i = 1; i < len; 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[len - 1][1];
 };
 ​
 // 贪心解法
 var maxProfit_greedy_ii = function(prices) {
     let totalProfit = 0;
     for (let i = 1; i < prices.length; i++) {
         if (prices[i] > prices[i - 1]) {
             totalProfit += prices[i] - prices[i - 1];
         }
     }
     return totalProfit;
 };

3. 买卖股票的最佳时机 III (LeetCode 123)

问题描述:

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你最多可以完成 两笔交易。注意:你不能同时参与多笔交易(即,在再次购买前,你必须卖出之前的股票)。这就像是"限量版"交易,机会只有两次,可得好好把握!💰💰

动态规划思路

这道题的难度在于"最多两笔交易"的限制。这意味着我们不能像第二题那样无脑贪心。我们需要更精细地定义状态。

我们可以定义五种状态来表示不同交易阶段的最大利润:

  • dp[i][0]:第 i 天不进行任何操作(或者说,没有进行任何交易)的最大利润。始终为0。
  • dp[i][1]:第 i 天第一次持有股票的最大利润。
  • dp[i][2]:第 i 天第一次不持有股票(即完成第一次交易)的最大利润。
  • dp[i][3]:第 i 天第二次持有股票的最大利润。
  • dp[i][4]:第 i 天第二次不持有股票(即完成第二次交易)的最大利润。

状态转移方程:

  • dp[i][0] = 0 (或者 dp[i-1][0]):不操作,利润为0。
  • dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] - prices[i]):第一次持有股票,可能是前一天就持有,或者今天买入(从不操作状态买入)。
  • dp[i][2] = Math.max(dp[i-1][2], dp[i-1][1] + prices[i]):第一次不持有股票,可能是前一天就不持有,或者今天卖出(从第一次持有状态卖出)。
  • dp[i][3] = Math.max(dp[i-1][3], dp[i-1][2] - prices[i]):第二次持有股票,可能是前一天就持有,或者今天买入(从第一次不持有状态买入)。
  • dp[i][4] = Math.max(dp[i-1][4], dp[i-1][3] + prices[i]):第二次不持有股票,可能是前一天就不持有,或者今天卖出(从第二次持有状态卖出)。

初始化:

  • dp[0][0] = 0
  • dp[0][1] = -prices[0] (第一次买入)
  • dp[0][2] = 0 (第一次卖出,还没发生,所以是0)
  • dp[0][3] = -prices[0] (第二次买入,还没发生,所以是第一次买入的利润)
  • dp[0][4] = 0 (第二次卖出,还没发生,所以是0)

最终结果是 dp[len-1][4],因为我们希望完成两笔交易并获得最大利润。

代码示例

js 复制代码
 var maxProfit_iii = function(prices) {
     const len = prices.length;
     if (len === 0) return 0;
 ​
     // dp[i][0]: 不操作
     // dp[i][1]: 第一次持有
     // dp[i][2]: 第一次不持有
     // dp[i][3]: 第二次持有
     // dp[i][4]: 第二次不持有
     let dp = new Array(len).fill(0).map(() => new Array(5).fill(0));
 ​
     dp[0][1] = -prices[0];
     dp[0][3] = -prices[0];
 ​
     for (let i = 1; i < len; i++) {
         dp[i][0] = dp[i - 1][0]; // 保持不操作状态
         dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
         dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]);
         dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
         dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
     }
 ​
     return dp[len - 1][4];
 };

4. 买卖股票的最佳时机 IV (LeetCode 188)

问题描述:

给定一个整数数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。给定一个整数 k,你可以最多完成 k 笔交易。注意:你不能同时参与多笔交易(即,在再次购买前,你必须卖出之前的股票)。这道题是前面几道题的"集大成者",它把交易次数从固定值变成了变量 k。感觉像是在玩一个"无限次"的交易游戏,但突然告诉你,你只有 k 次机会!😱

动态规划思路

这道题是 LeetCode 123 的泛化版本。当 k 足够大时(例如 k >= prices.length / 2),它就退化成了 LeetCode 122(可以进行无限次交易)。因此,我们需要对 k 进行特殊处理。

我们可以定义 dp[i][j][0] 表示第 i 天最多进行 j 次交易且不持有股票的最大利润,dp[i][j][1] 表示第 i 天最多进行 j 次交易且持有股票的最大利润。

  • dp[i][j][0]

    • 如果第 i 天不持有股票,且完成了 j 次交易,那么可能是前一天就不持有(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])
  • dp[i][j][1]

    • 如果第 i 天持有股票,且完成了 j 次交易,那么可能是前一天就持有(dp[i-1][j][1]),或者今天买入(dp[i-1][j-1][0] - prices[i])。注意,这里买入时,交易次数 j 已经包含了这次买入,所以是从 j-1 次交易且不持有股票的状态买入。
    • 因此,dp[i][j][1] = Math.max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i])

初始化:

  • dp[0][j][0] = 0 (第0天不持有股票,利润为0)
  • dp[0][j][1] = -prices[0] (第0天持有股票,利润为负的第0天价格)

最终结果是 dp[n-1][k][0]

代码示例

js 复制代码
 var maxProfit_iv = function(k, prices) {
     const n = prices.length;
     if (n === 0 || k === 0) return 0;
 ​
     // 优化:当k大于n/2时,相当于可以进行无限次交易
     // 因为一次买卖至少需要两天,所以最多进行 n/2 次交易
     if (k >= Math.floor(n / 2)) {
         let profit = 0;
         for (let i = 1; i < n; i++) {
             if (prices[i] > prices[i - 1]) {
                 profit += prices[i] - prices[i - 1];
             }
         }
         return profit;
     }
 ​
     // dp[i][j][0]表示第i天最多进行j次交易且不持有股票的最大利润
     // dp[i][j][1]表示第i天最多进行j次交易且持有股票的最大利润
     let dp = new Array(n).fill(0).map(() => {
         return new Array(k + 1).fill(0).map(() => {
             return [0, 0];
         });
     });
 ​
     // 初始化dp数组
     for (let j = 1; j <= k; j++) {
         dp[0][j][1] = -prices[0];
     }
 ​
     // 状态转移
     for (let i = 1; i < n; i++) {
         for (let j = 1; j <= k; j++) {
             dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
             dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
         }
     }
 ​
     return dp[n - 1][k][0];
 };

5. 买卖股票的最佳时机含冷冻期 (LeetCode 309)

问题描述:

给定一个整数数组 prices,其中 prices[i] 表示第 i 天的股票价格 。设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能多地完成交易(多次买卖一支股票):

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

这就像是"交易暂停"!你刚赚了一笔,正准备乘胜追击,结果系统告诉你:"抱歉,您需要休息一天才能继续交易!" 真是让人又爱又恨的冷冻期啊!🥶

动态规划思路

这道题引入了一个新的状态------"冷冻期"。我们需要在DP状态中体现这个限制。

我们可以定义三种状态:

  • dp[i][0]:第 i 天持有股票的最大利润。
  • dp[i][1]:第 i 天卖出股票的最大利润(卖出后进入冷冻期)。
  • dp[i][2]:第 i 天处于冷冻期或不持有股票且非冷冻期的最大利润(即可以买入股票的状态)。

状态转移方程:

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

    • i 天持有股票,可能是前一天就持有,或者从"可以买入"的状态(dp[i-1][2])买入。
  • dp[i][1] = dp[i-1][0] + prices[i]

    • i 天卖出股票,只能是前一天持有股票,今天卖出。
  • dp[i][2] = Math.max(dp[i-1][2], dp[i-1][1])

    • i 天处于"可以买入"的状态,可能是前一天就处于这个状态,或者前一天卖出股票后进入冷冻期(dp[i-1][1])。

初始化:

  • dp[0][0] = -prices[0] (第一天买入股票)
  • dp[0][1] = 0 (第一天不可能卖出)
  • dp[0][2] = 0 (第一天不持有且非冷冻期)

最终结果是 Math.max(dp[n-1][1], dp[n-1][2]),因为最终利润肯定是不持有股票的状态。

代码示例

js 复制代码
 var maxProfit_cold_period = function(prices) {
     const n = prices.length;
     if (n < 2) return 0;
 ​
     // dp[i][0]: 第i天持有股票的最大利润
     // dp[i][1]: 第i天卖出股票的最大利润
     // dp[i][2]: 第i天处于冷冻期或不持有股票且非冷冻期的最大利润
     let dp = new Array(n).fill(0).map(() => new Array(3).fill(0));
 ​
     dp[0][0] = -prices[0];
     dp[0][1] = 0;
     dp[0][2] = 0;
 ​
     for (let i = 1; i < n; i++) {
         dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2] - prices[i]);
         dp[i][1] = dp[i - 1][0] + prices[i];
         dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1]);
     }
 ​
     return Math.max(dp[n - 1][1], dp[n - 1][2]);
 };

6. 买卖股票的最佳时机含手续费 (LeetCode 714)

问题描述:

给定一个整数数组 prices,其中 prices[i] 表示第 i 天的股票价格 ;整数 fee 代表了交易手续费用。你可以尽可能多地完成交易。注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。每次交易你都需要支付 fee 的手续费。这就像是"隐形税收"!每次交易都要被"抽成",这可得好好算算,别忙活半天,利润全交手续费了!💸

动态规划思路

这道题与 LeetCode 122 类似,都是可以进行多次交易。唯一的区别在于,每次卖出股票时,需要支付一笔手续费。

我们仍然定义两种状态:

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

状态转移方程:

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

    • i 天持有股票,可能是前一天就持有,或者从不持有状态买入。
  • dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] + prices[i] - fee)

    • i 天不持有股票,可能是前一天就不持有,或者从持有状态卖出。注意,这里卖出时需要减去手续费 fee

初始化:

  • dp[0][0] = -prices[0] (第一天买入股票)
  • dp[0][1] = 0 (第一天不持有股票)

最终结果是 dp[n-1][1]

代码示例

js 复制代码
 var maxProfit_fee = function(prices, fee) {
     const n = prices.length;
     if (n === 0) return 0;
 ​
     // dp[i][0]: 第i天持有股票的最大利润
     // dp[i][1]: 第i天不持有股票的最大利润
     let dp = new Array(n).fill(0).map(() => new Array(2).fill(0));
 ​
     dp[0][0] = -prices[0];
     dp[0][1] = 0;
 ​
     for (let 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][0] + prices[i] - fee);
     }
 ​
     return dp[n - 1][1];
 };

股票DP问题对比分析:异同点一览

经过上面六道题的"洗礼",你是不是对股票DP问题有了更深入的理解?为了让你一眼看穿它们的"庐山真面目",我特意为你准备了一份"武功秘籍"------对比表格!请看:

题目 交易次数限制 特殊条件 DP状态数 核心思想 时间复杂度 空间复杂度
121. 买卖股票的最佳时机 最多1次 2个状态 记录最低价格和最大利润 O(n) O(1)
122. 买卖股票的最佳时机 II 无限次 2个状态 贪心:每次上涨都交易 O(n) O(1)
123. 买卖股票的最佳时机 III 最多2次 5个状态 分别记录两次交易的状态 O(n) O(1)
188. 买卖股票的最佳时机 IV 最多k次 k可能很大时退化为无限次交易 2k+1个状态 通用k次交易模板 O(nk) O(k)
309. 买卖股票的最佳时机含冷冻期 无限次 卖出后冷冻1天 3-4个状态 增加冷冻期状态 O(n) O(1)
714. 买卖股票的最佳时机含手续费 无限次 每次交易有手续费 2个状态 卖出时扣除手续费 O(n) O(1)

从这个表格中,我们可以清晰地看到:

  • 交易次数限制 是区分这些问题的关键。从"一次"到"无限次"再到"k次",DP状态的定义也随之变化。
  • 特殊条件(冷冻期、手续费)会增加额外的状态维度或修改状态转移方程。
  • DP状态数 随着问题的复杂性而增加,但核心思想都是围绕"持有"和"不持有"股票这两种基本状态展开。
  • 时间复杂度和空间复杂度 在很多情况下都可以通过"滚动数组"等优化手段降低,实现O(1)的空间复杂度。

是不是感觉豁然开朗了?😎

动态规划思想升华:从股票问题看DP的本质

经过这六道股票问题的"实战演练",你是否对动态规划有了更深刻的理解?这些问题虽然形式各异,但其背后都蕴含着动态规划的精髓。总结一下,动态规划在解决这类问题时,通常遵循以下"三板斧":

确定状态

这是动态规划的第一步,也是最关键的一步。你需要明确 dp 数组的含义,它代表了什么?在股票问题中,dp[i][j] 通常表示在第 i 天,处于某种状态 j 时的最大利润。状态的定义要能够覆盖所有可能的情况,并且能够方便地进行状态转移。比如,我们不仅要考虑"持有"和"不持有"股票,还要考虑"交易次数"、"冷冻期"等特殊条件,把它们都融入到状态定义中。

确定状态转移方程

状态转移方程是动态规划的"灵魂"。它描述了如何从已知的子问题解推导出当前问题的解。在股票问题中,状态转移方程通常是 max 函数的组合,表示在当前状态下,我们可以通过哪些前一天的状态转移过来,并选择其中利润最大的。这就像是在做选择题,每一步都要选择最优的路径,才能最终达到最大利润的"彼岸"。

确定初始化和遍历顺序

初始化是为 dp 数组的边界条件赋值,为后续的状态转移提供"起点"。遍历顺序则决定了我们计算 dp 数组的先后次序,确保在计算 dp[i] 时,所有依赖的 dp[i-1] 或其他更早的状态都已经计算完毕。在股票问题中,通常是从第0天开始,逐天向后遍历。

DP核心思想提炼

从股票问题中,我们还能提炼出DP的几个核心思想:

  • 最优子结构: 股票问题的最大利润,可以通过子问题的最大利润推导出来。比如,今天的最大利润,取决于昨天的最大利润加上今天的操作。
  • 无后效性: 今天的决策只影响未来的状态,而不会影响过去的状态。我们不需要关心过去是如何达到某个状态的,只需要知道那个状态的最大利润是多少。
  • 重叠子问题: 很多子问题会被重复计算。动态规划通过"记忆化"或填充 dp 表的方式,避免了重复计算,大大提高了效率。

所以,当你下次再遇到类似的优化问题时,不妨想想动态规划的这"三板斧",也许就能找到解题的突破口。动态规划不仅仅是一种算法,更是一种解决问题的思维方式。掌握了它,你就能在算法的海洋里,乘风破浪,所向披靡!🌊

结语:DP在手,天下我有!

恭喜你!🎉 经过这番"股票DP"的深度学习,你已经掌握了解决六道经典股票买卖问题的动态规划方法。从最初的"一次交易"到复杂的"k次交易"、"冷冻期"和"手续费",我们一步步揭示了这些问题背后的DP奥秘。你不仅学会了如何定义状态、推导状态转移方程,更重要的是,你体会到了动态规划在解决这类问题时的强大之处。

动态规划就像一把瑞士军刀,虽然看起来复杂,但一旦掌握了它的使用方法,就能解决各种各样的问题。在算法的世界里,它无处不在,从背包问题到最长公共子序列,再到我们今天探讨的股票问题,DP的身影随处可见。希望通过本文,你不仅能轻松应对面试中的股票DP问题,更能将这种"化繁为简,步步为营"的思维方式运用到你解决其他问题的过程中。

题目链接

1\] LeetCode 121. 买卖股票的最佳时机: [leetcode.cn/problems/be...](https://link.juejin.cn?target=https%3A%2F%2Fleetcode.cn%2Fproblems%2Fbest-time-to-buy-and-sell-stock%2F "https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/") \[2\] LeetCode 122. 买卖股票的最佳时机 II: [leetcode.cn/problems/be...](https://link.juejin.cn?target=https%3A%2F%2Fleetcode.cn%2Fproblems%2Fbest-time-to-buy-and-sell-stock-ii%2F "https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/") \[3\] LeetCode 123. 买卖股票的最佳时机 III: [leetcode.cn/problems/be...](https://link.juejin.cn?target=https%3A%2F%2Fleetcode.cn%2Fproblems%2Fbest-time-to-buy-and-sell-stock-iii%2F "https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/") \[4\] LeetCode 188. 买卖股票的最佳时机 IV: [leetcode.cn/problems/be...](https://link.juejin.cn?target=https%3A%2F%2Fleetcode.cn%2Fproblems%2Fbest-time-to-buy-and-sell-stock-iv%2F "https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/") \[ 5\] LeetCode 309. 买卖股票的最佳时机含冷冻期: [leetcode.cn/problems/be...](https://link.juejin.cn?target=https%3A%2F%2Fleetcode.cn%2Fproblems%2Fbest-time-to-buy-and-sell-stock-with-cooldown%2F "https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/") \[6\] LeetCode 714. 买卖股票的最佳时机含手续费: [leetcode.cn/problems/be...](https://link.juejin.cn?target=https%3A%2F%2Fleetcode.cn%2Fproblems%2Fbest-time-to-buy-and-sell-stock-with-transaction-fee%2F "https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/")

相关推荐
天涯学馆23 分钟前
为什么越来越多开发者偷偷用上了 Svelte?
前端·javascript·svelte
拾光拾趣录34 分钟前
为什么浏览器那条“假进度”救不了我们?
前端·javascript·浏览器
智者知已应修善业36 分钟前
【C# 找最大值、最小值和平均值及大于个数和值】2022-9-23
经验分享·笔记·算法·c#
香菜狗39 分钟前
vue3响应式数据(ref,reactive)详解
前端·javascript·vue.js
Zz_waiting.1 小时前
Java 算法解析 - 双指针
java·开发语言·数据结构·算法·leetcode·双指针
油丶酸萝卜别吃1 小时前
SSE与Websocket有什么区别?
前端·javascript·网络·网络协议
27669582921 小时前
拼多多小程序 anti_content 分析
java·javascript·python·node·拼多多·anti-content·anti_content
The_era_achievs_hero2 小时前
uni-appDay02
javascript·vue.js·微信小程序·uni-app
rzl022 小时前
SpringBoot6-10(黑马)
linux·前端·javascript
overFitBrain2 小时前
数据结构-4(常用排序算法、二分查找)
linux·数据结构·算法