You are given an integer array prices where prices[i] is the price of a given stock on the ith day.
On each day, you may decide to buy and/or sell the stock. However, you can buy it then immediately sell it on the same day. Also, you are allowed to perform any number of transactions but can hold at most one share of the stock at any time.
Find and return the maximum profit you can achieve.
Example 1:
Input: prices = [7,1,5,3,6,4]
Output: 7Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4. Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3. Total profit is 4 + 3 = 7.
Example 2:
Input: prices = [1,2,3,4,5]
Output: 4Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4. Total profit is 4.
Constraints:
1 <= prices.length <= 30,0000 <= prices[i] <= 10,000Before attempting this problem, you should be comfortable with:
At each day, we have a choice: if we are not holding stock, we can either buy or skip. If we are holding stock, we can either sell or skip. We want to maximize profit by exploring all possible combinations of buy and sell decisions. This naturally leads to a recursive approach where we try both options at each step and return the maximum profit.
rec(i, bought) where i is the current day and bought indicates if we are holding stock.0.bought = true), we can sell at the current price and add it to our profit.bought = false), we can buy at the current price (subtract it from profit).Input: prices = [7, 1, 5, 3, 6, 4]
Price Array:
āāāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā
ā 7 ā 1 ā 5 ā 3 ā 6 ā 4 ā
āāāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā
[0] [1] [2] [3] [4] [5]
Visualization:
BUY BUY
ā ā
āāāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā
ā 7 ā 1 ā 5 ā 3 ā 6 ā 4 ā
āāāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā
0 1 2 3 4 5 ā day
ā ā
SELL SELL
Transaction Flow:
Day 1: BUY at $1 āāāāāāāāāāāāāāāāāāāā®
ā Profit: $5 - $1 = $4
Day 2: SELL at $5 āāāāāāāāāāāāāāāāāāāÆ
Day 3: BUY at $3 āāāāāāāāāāāāāāāāāāāā®
ā Profit: $6 - $3 = $3
Day 4: SELL at $6 āāāāāāāāāāāāāāāāāāāÆ
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā
ā Total Profit: $4 + $3 = $7 ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
The recursion explores all possible buy/sell decisions:
rec(0, False) - At day 0, not holding stock
āāā Skip day 0: rec(1, False)
ā āāā Buy at day 1 (price=1): -1 + rec(2, True)
ā ā āāā Sell at day 2 (price=5): 5 + rec(3, False) ā profit = 4
ā ā ā āāā Buy at day 3: -3 + rec(4, True)
ā ā ā ā āāā Sell at day 4: 6 + rec(5, False) ā profit = 3
ā ā ā ā āāā Total path: 4 + 3 = 7 ā (Optimal)
ā ā ā āāā Skip, buy at day 4, etc...
ā ā āāā Skip day 2, sell at day 3, etc...
ā āāā Skip day 1: rec(2, False)...
āāā Buy at day 0 (price=7): -7 + rec(1, True)
āāā Sell at day 1 (price=1): 1 + rec(2, False) ā profit = -6 (bad choice)
Optimal Path Found - Step by Step:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Step 1: Day 1 - BUY ā
ā ā
ā Action: Buy at price 1 ā
ā Balance: -1 ā
ā Status: Holding stock ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Step 2: Day 2 - SELL ā
ā ā
ā Action: Sell at price 5 ā
ā Balance: -1 + 5 = 4 ā
ā Status: Not holding stock ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Step 3: Day 3 - BUY ā
ā ā
ā Action: Buy at price 3 ā
ā Balance: 4 - 3 = 1 ā
ā Status: Holding stock ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Step 4: Day 4 - SELL ā
ā ā
ā Action: Sell at price 6 ā
ā Balance: 1 + 6 = 7 ā
ā Status: Not holding stock ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Summary:
āāāāāāāāāāāā¬āāāāāāāāāāāāā¬āāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāā
ā Day ā Price ā Action ā Balance ā
āāāāāāāāāāāā¼āāāāāāāāāāāāā¼āāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāā¤
ā 1 ā 1 ā BUY ā -1 ā
ā 2 ā 5 ā SELL ā 4 ā
ā 3 ā 3 ā BUY ā 1 ā
ā 4 ā 6 ā SELL ā 7 ā
āāāāāāāāāāāā“āāāāāāāāāāāāā“āāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāā
Final Answer: 7class Solution:
def maxProfit(self, prices: List[int]) -> int:
def rec(i, bought):
if i == len(prices):
return 0
res = rec(i + 1, bought)
if bought:
res = max(res, prices[i] + rec(i + 1, False))
else:
res = max(res, -prices[i] + rec(i + 1, True))
return res
return rec(0, False)The recursive solution recalculates the same states multiple times. For example, the state (day 3, not holding) might be reached through different paths. By storing computed results in a memoization table, we avoid redundant work. Each unique state (day, holding_status) is computed only once.
rec(i, bought) that first checks if the result is already cached.0 with no stock held.Input: prices = [7, 1, 5, 3, 6, 4]
Price Array:
āāāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā
ā 7 ā 1 ā 5 ā 3 ā 6 ā 4 ā
āāāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā
[0] [1] [2] [3] [4] [5]
Visualization:
BUY BUY
ā ā
āāāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā
ā 7 ā 1 ā 5 ā 3 ā 6 ā 4 ā
āāāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā
0 1 2 3 4 5 ā day
ā ā
SELL SELL
Transaction Flow:
Day 1: BUY at $1 āāāāāāāāāāāāāāāāāāāā®
ā Profit: $5 - $1 = $4
Day 2: SELL at $5 āāāāāāāāāāāāāāāāāāāÆ
Day 3: BUY at $3 āāāāāāāāāāāāāāāāāāāā®
ā Profit: $6 - $3 = $3
Day 4: SELL at $6 āāāāāāāāāāāāāāāāāāāÆ
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā
ā Total Profit: $4 + $3 = $7 ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Memoization avoids recomputing the same states.
dp[i][bought] stores max profit from day i with bought status.
Call Tree with Memoization:
rec(0, 0) ā not cached
āāā rec(1, 0) ā not cached
ā āāā rec(2, 0) ā not cached
ā ā āāā rec(3, 0) ā not cached
ā ā ā āāā rec(4, 0) ā not cached
ā ā ā ā āāā rec(5, 0) = 0, rec(5, 1) = 0
ā ā ā ā dp[4][0] = max(0, -6+0) = 0
ā ā ā ā dp[4][1] = max(0, 6+0) = 6
ā ā ā āāā dp[3][0] = max(dp[4][0], -3+dp[4][1]) = max(0, 3) = 3
ā ā ā dp[3][1] = max(dp[4][1], 3+dp[4][0]) = max(6, 3) = 6
ā ā āāā dp[2][0] = max(dp[3][0], -5+dp[3][1]) = max(3, 1) = 3
ā ā dp[2][1] = max(dp[3][1], 5+dp[3][0]) = max(6, 8) = 8
ā āāā dp[1][0] = max(dp[2][0], -1+dp[2][1]) = max(3, 7) = 7
ā dp[1][1] = max(dp[2][1], 1+dp[2][0]) = max(8, 4) = 8
āāā dp[0][0] = max(dp[1][0], -7+dp[1][1]) = max(7, 1) = 7
DP State Computation - Step by Step:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Base Case: Day 5 (beyond array) ā
ā ā
ā dp[5][0] = 0 (can buy, but no days left) ā
ā dp[5][1] = 0 (can sell, but no days left) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Day 4: price = 6 ā
ā ā
ā dp[4][0] = max(dp[5][0], -6 + dp[5][1]) ā
ā = max(0, -6 + 0) = 0 ā
ā ā
ā dp[4][1] = max(dp[5][1], 6 + dp[5][0]) ā
ā = max(0, 6 + 0) = 6 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Day 3: price = 3 ā
ā ā
ā dp[3][0] = max(dp[4][0], -3 + dp[4][1]) ā
ā = max(0, -3 + 6) = 3 ā
ā ā
ā dp[3][1] = max(dp[4][1], 3 + dp[4][0]) ā
ā = max(6, 3 + 0) = 6 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Day 2: price = 5 ā
ā ā
ā dp[2][0] = max(dp[3][0], -5 + dp[3][1]) ā
ā = max(3, -5 + 6) = 3 ā
ā ā
ā dp[2][1] = max(dp[3][1], 5 + dp[3][0]) ā
ā = max(6, 5 + 3) = 8 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Day 1: price = 1 ā
ā ā
ā dp[1][0] = max(dp[2][0], -1 + dp[2][1]) ā
ā = max(3, -1 + 8) = 7 ā
ā ā
ā dp[1][1] = max(dp[2][1], 1 + dp[2][0]) ā
ā = max(8, 1 + 3) = 8 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Day 0: price = 7 ā
ā ā
ā dp[0][0] = max(dp[1][0], -7 + dp[1][1]) ā
ā = max(7, -7 + 8) = 7 ā
ā ā
ā dp[0][1] = max(dp[1][1], 7 + dp[1][0]) ā
ā = max(8, 7 + 7) = 14 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Final DP Table:
āāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāā
ā Day ā bought=0 (can buy) ā bought=1 (can sell) ā
āāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā 0 ā 7 ā 14 ā
ā 1 ā 7 ā 8 ā
ā 2 ā 3 ā 8 ā
ā 3 ā 3 ā 6 ā
ā 4 ā 0 ā 6 ā
ā 5 ā 0 ā 0 ā
āāāāāāāāāāā“āāāāāāāāāāāāāāāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāāāāāāāāāā
Final Answer: dp[0][0] = 7class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp = {}
def rec(i, bought):
if i == len(prices):
return 0
if (i, bought) in dp:
return dp[(i, bought)]
res = rec(i + 1, bought)
if bought:
res = max(res, prices[i] + rec(i + 1, False))
else:
res = max(res, -prices[i] + rec(i + 1, True))
dp[(i, bought)] = res
return res
return rec(0, False)Instead of solving recursively from day 0 forward, we can build the solution iteratively from the last day backward. At each day, we compute the maximum profit for both states (holding and not holding stock) using the already-computed values for the next day. This eliminates recursion overhead.
dp array where dp[i][0] is the max profit from day i when we can buy, and dp[i][1] is the max profit when we can sell.dp[n][0] = dp[n][1] = 0 (no profit after the last day).dp[i][0] as the max of skipping or buying (subtract price, transition to sell state).dp[i][1] as the max of skipping or selling (add price, transition to buy state).dp[0][0] as we start without holding any stock.Input: prices = [7, 1, 5, 3, 6, 4]
Price Array:
āāāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā
ā 7 ā 1 ā 5 ā 3 ā 6 ā 4 ā
āāāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā
[0] [1] [2] [3] [4] [5]
Visualization:
BUY BUY
ā ā
āāāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā
ā 7 ā 1 ā 5 ā 3 ā 6 ā 4 ā
āāāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā
0 1 2 3 4 5 ā day
ā ā
SELL SELL
Transaction Flow:
Day 1: BUY at $1 āāāāāāāāāāāāāāāāāāāā®
ā Profit: $5 - $1 = $4
Day 2: SELL at $5 āāāāāāāāāāāāāāāāāāāÆ
Day 3: BUY at $3 āāāāāāāāāāāāāāāāāāāā®
ā Profit: $6 - $3 = $3
Day 4: SELL at $6 āāāāāāāāāāāāāāāāāāāÆ
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā
ā Total Profit: $4 + $3 = $7 ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Building DP table from right to left:
- dp[i][0] = max profit from day i when we CAN buy
- dp[i][1] = max profit from day i when we CAN sell (holding stock)
Step-by-Step DP Table Construction:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Base Case: i = 6 (beyond array) ā
ā ā
ā dp[6][0] = 0 (base case) ā
ā dp[6][1] = 0 (base case) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā i = 5 (price = 4): ā
ā ā
ā dp[5][0] = max(dp[6][0], -4 + dp[6][1]) ā
ā = max(0, -4 + 0) = 0 (skip buying) ā
ā ā
ā dp[5][1] = max(dp[6][1], 4 + dp[6][0]) ā
ā = max(0, 4 + 0) = 4 (sell at price 4) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā i = 4 (price = 6): ā
ā ā
ā dp[4][0] = max(dp[5][0], -6 + dp[5][1]) ā
ā = max(0, -6 + 4) = 0 (skip buying) ā
ā ā
ā dp[4][1] = max(dp[5][1], 6 + dp[5][0]) ā
ā = max(4, 6 + 0) = 6 (sell at price 6) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā i = 3 (price = 3): ā
ā ā
ā dp[3][0] = max(dp[4][0], -3 + dp[4][1]) ā
ā = max(0, -3 + 6) = 3 (buy at price 3) ā
ā ā
ā dp[3][1] = max(dp[4][1], 3 + dp[4][0]) ā
ā = max(6, 3 + 0) = 6 (keep holding) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā i = 2 (price = 5): ā
ā ā
ā dp[2][0] = max(dp[3][0], -5 + dp[3][1]) ā
ā = max(3, -5 + 6) = 3 (skip buying) ā
ā ā
ā dp[2][1] = max(dp[3][1], 5 + dp[3][0]) ā
ā = max(6, 5 + 3) = 8 (sell at price 5) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā i = 1 (price = 1): ā
ā ā
ā dp[1][0] = max(dp[2][0], -1 + dp[2][1]) ā
ā = max(3, -1 + 8) = 7 (buy at price 1) ā
ā ā
ā dp[1][1] = max(dp[2][1], 1 + dp[2][0]) ā
ā = max(8, 1 + 3) = 8 (keep holding) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā i = 0 (price = 7): ā
ā ā
ā dp[0][0] = max(dp[1][0], -7 + dp[1][1]) ā
ā = max(7, -7 + 8) = 7 (skip buying) ā
ā ā
ā dp[0][1] = max(dp[1][1], 7 + dp[1][0]) ā
ā = max(8, 7 + 7) = 14 (would sell, but unused) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Final DP Table:
āāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Day ā 0 1 2 3 4 5 6 ā
āāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā dp[i][0]ā 7 7 3 3 0 0 0 ā
ā (buy) ā ā
āāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā dp[i][1]ā 14 8 8 6 6 4 0 ā
ā (sell) ā ā
āāāāāāāāāāā“āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Final Answer: dp[0][0] = 7class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
dp = [[0] * 2 for _ in range(n + 1)]
for i in range(n - 1, -1, -1):
dp[i][0] = max(dp[i + 1][0], -prices[i] + dp[i + 1][1])
dp[i][1] = max(dp[i + 1][1], prices[i] + dp[i + 1][0])
return dp[0][0]Looking at the bottom-up solution, we notice that to compute the values for day i, we only need the values from day i+1. We do not need the entire dp array. This means we can reduce space from O(n) to O(1) by using just four variables to track the current and next day's states.
nextBuy, nextSell (for day i+1), and curBuy, curSell (for day i).0.curBuy as the max of skipping (nextBuy) or buying (-price + nextSell).curSell as the max of skipping (nextSell) or selling (price + nextBuy).curBuy as the final answer.Input: prices = [7, 1, 5, 3, 6, 4]
Price Array:
āāāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā
ā 7 ā 1 ā 5 ā 3 ā 6 ā 4 ā
āāāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā
[0] [1] [2] [3] [4] [5]
Visualization:
BUY BUY
ā ā
āāāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā
ā 7 ā 1 ā 5 ā 3 ā 6 ā 4 ā
āāāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā
0 1 2 3 4 5 ā day
ā ā
SELL SELL
Transaction Flow:
Day 1: BUY at $1 āāāāāāāāāāāāāāāāāāāā®
ā Profit: $5 - $1 = $4
Day 2: SELL at $5 āāāāāāāāāāāāāāāāāāāÆ
Day 3: BUY at $3 āāāāāāāāāāāāāāāāāāāā®
ā Profit: $6 - $3 = $3
Day 4: SELL at $6 āāāāāāāāāāāāāāāāāāāÆ
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā
ā Total Profit: $4 + $3 = $7 ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Using only 4 variables instead of full DP array:
- nextBuy / curBuy: max profit when we can buy
- nextSell / curSell: max profit when we can sell
Initial State:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā nextBuy = 0 ā
ā nextSell = 0 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Processing from right to left:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā i = 5 (price = 4): ā
ā ā
ā curBuy = max(nextBuy, -price + nextSell) ā
ā = max(0, -4 + 0) = 0 ā
ā ā
ā curSell = max(nextSell, price + nextBuy) ā
ā = max(0, 4 + 0) = 4 ā
ā ā
ā Update: nextBuy <- 0, nextSell <- 4 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā i = 4 (price = 6): ā
ā ā
ā curBuy = max(0, -6 + 4) = 0 ā
ā curSell = max(4, 6 + 0) = 6 ā
ā ā
ā Update: nextBuy <- 0, nextSell <- 6 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā i = 3 (price = 3): ā
ā ā
ā curBuy = max(0, -3 + 6) = 3 <-- Buy here for profit! ā
ā curSell = max(6, 3 + 0) = 6 ā
ā ā
ā Update: nextBuy <- 3, nextSell <- 6 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā i = 2 (price = 5): ā
ā ā
ā curBuy = max(3, -5 + 6) = 3 ā
ā curSell = max(6, 5 + 3) = 8 <-- Sell here! ā
ā ā
ā Update: nextBuy <- 3, nextSell <- 8 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā i = 1 (price = 1): ā
ā ā
ā curBuy = max(3, -1 + 8) = 7 <-- Best: buy at 1! ā
ā curSell = max(8, 1 + 3) = 8 ā
ā ā
ā Update: nextBuy <- 7, nextSell <- 8 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā i = 0 (price = 7): ā
ā ā
ā curBuy = max(7, -7 + 8) = 7 ā
ā curSell = max(8, 7 + 7) = 14 ā
ā ā
ā Final: curBuy = 7 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Variable Trace Table:
āāāāāāāāāāāā¬āāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāāā¬āāāāāāāāāāāā¬āāāāāāāāāāāāā
ā i ā price ā curBuy ā curSell ā nextBuy ā nextSell ā
āāāāāāāāāāāā¼āāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāāā¼āāāāāāāāāāāā¼āāāāāāāāāāāāā¤
ā init ā - ā 0 ā 0 ā 0 ā 0 ā
ā 5 ā 4 ā 0 ā 4 ā 0 ā 4 ā
ā 4 ā 6 ā 0 ā 6 ā 0 ā 6 ā
ā 3 ā 3 ā 3 ā 6 ā 3 ā 6 ā
ā 2 ā 5 ā 3 ā 8 ā 3 ā 8 ā
ā 1 ā 1 ā 7 ā 8 ā 7 ā 8 ā
ā 0 ā 7 ā 7 ā 14 ā 7 ā 8 ā
āāāāāāāāāāāā“āāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāāā“āāāāāāāāāāāā“āāāāāāāāāāāāā
Final Answer: curBuy = 7class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
next_buy = next_sell = 0
cur_buy = cur_sell = 0
for i in range(n - 1, -1, -1):
cur_buy = max(next_buy, -prices[i] + next_sell)
cur_sell = max(next_sell, prices[i] + next_buy)
next_buy = cur_buy
next_sell = cur_sell
return cur_buyThe key insight is that we can capture every upward price movement. If the price goes up from day i to day i+1, we can always "buy" on day i and "sell" on day i+1 to capture that profit. We do not need to track actual transactions because consecutive gains are equivalent to holding through multiple days. For example, buying at 1, holding through 3, 5, and selling at 6 gives the same profit as buying at 1, selling at 3, buying at 3, selling at 5, buying at 5, and selling at 6.
profit variable to 0.1 to the last day.profit.profit.Input: prices = [7, 1, 5, 3, 6, 4]
Price Array:
āāāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā
ā 7 ā 1 ā 5 ā 3 ā 6 ā 4 ā
āāāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā
[0] [1] [2] [3] [4] [5]
Visualization:
BUY BUY
ā ā
āāāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā¬āāāāāā
ā 7 ā 1 ā 5 ā 3 ā 6 ā 4 ā
āāāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā“āāāāāā
0 1 2 3 4 5 ā day
ā ā
SELL SELL
Transaction Flow:
Day 1: BUY at $1 āāāāāāāāāāāāāāāāāāāā®
ā Profit: $5 - $1 = $4
Day 2: SELL at $5 āāāāāāāāāāāāāāāāāāāÆ
Day 3: BUY at $3 āāāāāāāāāāāāāāāāāāāā®
ā Profit: $6 - $3 = $3
Day 4: SELL at $6 āāāāāāāāāāāāāāāāāāāÆ
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā
ā Total Profit: $4 + $3 = $7 ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Greedy Insight:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā
ā Capture EVERY upward movement! ā
ā ā
ā If tomorrow's price > today's price, that's profit we can take. ā
ā ā
ā We don't need to track actual buy/sell - just sum all positive gains. ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Greedy Approach - Step by Step:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Step 1: Compare day 0 -> day 1 ā
ā ā
ā prices[0] = 7, prices[1] = 1 ā
ā Change: 1 - 7 = -6 (negative, price dropped) ā
ā ā
ā Action: Skip (no profit) ā
ā Total Profit: 0 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Step 2: Compare day 1 -> day 2 ā
ā ā
ā prices[1] = 1, prices[2] = 5 ā
ā Change: 5 - 1 = +4 (positive! price increased) ā
ā ā
ā Action: Take profit! (Buy at 1, Sell at 5) ā
ā Total Profit: 0 + 4 = 4 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Step 3: Compare day 2 -> day 3 ā
ā ā
ā prices[2] = 5, prices[3] = 3 ā
ā Change: 3 - 5 = -2 (negative, price dropped) ā
ā ā
ā Action: Skip (no profit) ā
ā Total Profit: 4 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Step 4: Compare day 3 -> day 4 ā
ā ā
ā prices[3] = 3, prices[4] = 6 ā
ā Change: 6 - 3 = +3 (positive! price increased) ā
ā ā
ā Action: Take profit! (Buy at 3, Sell at 6) ā
ā Total Profit: 4 + 3 = 7 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Step 5: Compare day 4 -> day 5 ā
ā ā
ā prices[4] = 6, prices[5] = 4 ā
ā Change: 4 - 6 = -2 (negative, price dropped) ā
ā ā
ā Action: Skip (no profit) ā
ā Total Profit: 7 ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Summary Table:
āāāāāāāāā¬āāāāāāāāāā¬āāāāāāāāāāā¬āāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāā
ā Day ā Price ā Change ā Action ā Total Profit ā
āāāāāāāāā¼āāāāāāāāāā¼āāāāāāāāāāā¼āāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāā¤
ā 0 ā 7 ā - ā Start ā 0 ā
ā 1 ā 1 ā -6 ā Skip ā 0 ā
ā 2 ā 5 ā +4 ā Capture +4 ā 4 ā
ā 3 ā 3 ā -2 ā Skip ā 4 ā
ā 4 ā 6 ā +3 ā Capture +3 ā 7 ā
ā 5 ā 4 ā -2 ā Skip ā 7 ā
āāāāāāāāā“āāāāāāāāāā“āāāāāāāāāāā“āāāāāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāā
Transactions Made:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā
ā Transaction 1: ā
ā Buy @ $1 (day 1) ā
ā Sell @ $5 (day 2) ā
ā Profit: +$4 ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā Transaction 2: ā
ā Buy @ $3 (day 3) ā
ā Sell @ $6 (day 4) ā
ā Profit: +$3 ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā Total Profit: $4 + $3 = $7 ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Final Answer: 7This problem allows unlimited transactions, while Stock I only allows one. Using the Stock I approach (tracking min price and max difference) will undercount the total profit.
The greedy approach works because consecutive daily gains are equivalent to one larger transaction. Buying at 1, selling at 5 equals buying at 1, selling at 3, buying at 3, selling at 5. Missing this insight leads to overcomplicating the solution.
When using the greedy approach, only add the difference when prices[i] > prices[i-1]. Adding negative differences (price drops) reduces your profit incorrectly.
# Wrong: adds negative changes too
profit += prices[i] - prices[i - 1]
# Correct: only add positive gains
if prices[i] > prices[i - 1]:
profit += prices[i] - prices[i - 1]When comparing consecutive days, start the loop at index 1 (not 0) to safely access prices[i-1]. Starting at 0 causes an index out of bounds error.