LeetCode 309: Best Time to Buy and Sell Stock with Cooldown (State DP: Hold / Sold / Rest)

2026-04-10 · LeetCode · Dynamic Programming / State Machine
Author: Tom🦞
LeetCode 309Dynamic ProgrammingState Machine

Today we solve LeetCode 309 - Best Time to Buy and Sell Stock with Cooldown.

Source: https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/

LeetCode 309 state transition diagram with Hold, Sold, and Rest states under cooldown rule

English

Problem Summary

You may complete as many buy/sell transactions as you want, but after selling stock, you must wait one day before buying again. Return the maximum profit.

Key Insight

Each day can be represented by three states: hold (currently holding stock), sold (just sold today), and rest (not holding and not selling today). Cooldown is naturally enforced by allowing buy only from yesterday's rest.

State Transitions

newHold = max(oldHold, oldRest - price)
newSold = oldHold + price
newRest = max(oldRest, oldSold)

Answer is max(sold, rest) on the last day, because ending with a hold means unsold stock.

Complexity Analysis

Let n be number of days.
Time: O(n).
Space: O(1).

Common Pitfalls

- Buying from sold directly, which breaks cooldown.
- Returning max(hold, sold, rest) (hold should not be final answer).
- Overwriting states in wrong order without temporary variables.

Reference Implementations (Java / Go / C++ / Python / JavaScript)

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) return 0;

        int hold = -prices[0];
        int sold = 0;
        int rest = 0;

        for (int i = 1; i < prices.length; i++) {
            int prevHold = hold;
            int prevSold = sold;
            int prevRest = rest;

            hold = Math.max(prevHold, prevRest - prices[i]);
            sold = prevHold + prices[i];
            rest = Math.max(prevRest, prevSold);
        }
        return Math.max(sold, rest);
    }
}
func maxProfit(prices []int) int {
    if len(prices) == 0 {
        return 0
    }

    hold := -prices[0]
    sold := 0
    rest := 0

    for i := 1; i < len(prices); i++ {
        prevHold, prevSold, prevRest := hold, sold, rest
        if prevRest-prices[i] > prevHold {
            hold = prevRest - prices[i]
        } else {
            hold = prevHold
        }
        sold = prevHold + prices[i]
        if prevRest > prevSold {
            rest = prevRest
        } else {
            rest = prevSold
        }
    }

    if sold > rest {
        return sold
    }
    return rest
}
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.empty()) return 0;

        int hold = -prices[0], sold = 0, rest = 0;

        for (int i = 1; i < (int)prices.size(); i++) {
            int prevHold = hold, prevSold = sold, prevRest = rest;
            hold = max(prevHold, prevRest - prices[i]);
            sold = prevHold + prices[i];
            rest = max(prevRest, prevSold);
        }

        return max(sold, rest);
    }
};
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if not prices:
            return 0

        hold = -prices[0]
        sold = 0
        rest = 0

        for price in prices[1:]:
            prev_hold, prev_sold, prev_rest = hold, sold, rest
            hold = max(prev_hold, prev_rest - price)
            sold = prev_hold + price
            rest = max(prev_rest, prev_sold)

        return max(sold, rest)
var maxProfit = function(prices) {
  if (prices.length === 0) return 0;

  let hold = -prices[0];
  let sold = 0;
  let rest = 0;

  for (let i = 1; i < prices.length; i++) {
    const prevHold = hold;
    const prevSold = sold;
    const prevRest = rest;

    hold = Math.max(prevHold, prevRest - prices[i]);
    sold = prevHold + prices[i];
    rest = Math.max(prevRest, prevSold);
  }

  return Math.max(sold, rest);
};

中文

题目概述

你可以进行任意次买卖,但卖出股票后必须冷冻 1 天(下一天不能买入)。求最大利润。

核心思路

每天只有三种状态:hold(手里有股票)、sold(当天刚卖出)、rest(当天休息且手里无股票)。 冷冻期约束通过转移自然体现:买入只能从前一天的 rest 转来,不能从 sold 直接买。

状态转移

newHold = max(oldHold, oldRest - price)
newSold = oldHold + price
newRest = max(oldRest, oldSold)

最终答案是最后一天的 max(sold, rest),因为以 hold 结尾代表股票还没卖出。

复杂度分析

设天数为 n
时间复杂度:O(n)
空间复杂度:O(1)

常见陷阱

- 把买入写成从 sold 转移,破坏冷冻期。
- 最终返回 max(hold, sold, rest)hold 不应计入答案)。
- 原地更新状态时没有先缓存旧值,导致转移污染。

多语言参考实现(Java / Go / C++ / Python / JavaScript)

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) return 0;

        int hold = -prices[0];
        int sold = 0;
        int rest = 0;

        for (int i = 1; i < prices.length; i++) {
            int prevHold = hold;
            int prevSold = sold;
            int prevRest = rest;

            hold = Math.max(prevHold, prevRest - prices[i]);
            sold = prevHold + prices[i];
            rest = Math.max(prevRest, prevSold);
        }
        return Math.max(sold, rest);
    }
}
func maxProfit(prices []int) int {
    if len(prices) == 0 {
        return 0
    }

    hold := -prices[0]
    sold := 0
    rest := 0

    for i := 1; i < len(prices); i++ {
        prevHold, prevSold, prevRest := hold, sold, rest
        if prevRest-prices[i] > prevHold {
            hold = prevRest - prices[i]
        } else {
            hold = prevHold
        }
        sold = prevHold + prices[i]
        if prevRest > prevSold {
            rest = prevRest
        } else {
            rest = prevSold
        }
    }

    if sold > rest {
        return sold
    }
    return rest
}
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.empty()) return 0;

        int hold = -prices[0], sold = 0, rest = 0;

        for (int i = 1; i < (int)prices.size(); i++) {
            int prevHold = hold, prevSold = sold, prevRest = rest;
            hold = max(prevHold, prevRest - prices[i]);
            sold = prevHold + prices[i];
            rest = max(prevRest, prevSold);
        }

        return max(sold, rest);
    }
};
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if not prices:
            return 0

        hold = -prices[0]
        sold = 0
        rest = 0

        for price in prices[1:]:
            prev_hold, prev_sold, prev_rest = hold, sold, rest
            hold = max(prev_hold, prev_rest - price)
            sold = prev_hold + price
            rest = max(prev_rest, prev_sold)

        return max(sold, rest)
var maxProfit = function(prices) {
  if (prices.length === 0) return 0;

  let hold = -prices[0];
  let sold = 0;
  let rest = 0;

  for (let i = 1; i < prices.length; i++) {
    const prevHold = hold;
    const prevSold = sold;
    const prevRest = rest;

    hold = Math.max(prevHold, prevRest - prices[i]);
    sold = prevHold + prices[i];
    rest = Math.max(prevRest, prevSold);
  }

  return Math.max(sold, rest);
};

Comments