You are given an array prices where prices[i] is the price of a given stock on the ith day.
Find the maximum profit you can achieve. You may complete at most two transactions.
Note: You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again).
solutions
top-down recursion w/ memoization (2d)
This was the most obvious logical approach for me. At each index, we can either buy at the current index and sell at any future index (which we all try), or skip the index.
# O(n^2)
def maxProfit(self, prices: List[int]) -> int:
memo = {}
def recurse(idx, tx_completed):
if idx == len(prices) or tx_completed == 2:
return 0
if (idx, tx_completed) in memo:
return memo[(idx, tx_completed)]
best = 0
# buy on day idx, sell on any day afterwards
for i in range(idx+1, len(prices)):
if prices[i] > prices[idx]:
best = max(
best,
prices[i]-prices[idx]+recurse(i+1, tx_completed+1)
)
# don't buy on this day
best = max(best, recurse(idx+1, tx_completed))
memo[(idx, tx_completed)] = best
return best
return recurse(0, 0)top-down recursion w/ memoization (3d)
Instead of trying every selling time for each index, we can keep track of our current state (holding/not holding). Then, our two options at each index are either:
- Skip the index.
- If we’re holding a stock, sell it and continue. If we’re not, buy it and continue.
# O(n)
def maxProfit(self, prices: List[int]) -> int:
memo = {}
def recurse(idx, actions, holding):
if idx == len(prices) or actions == 0:
return 0
if (idx, actions, holding) in memo:
return memo[(idx, actions, holding)]
best = 0
# if we aren't holding, buy stock
if not holding:
best = max(
best,
recurse(idx+1, actions-1, not holding) - prices[idx]
)
else: # if we are holding, sell stock and move to next index
best = max(
best,
recurse(idx+1, actions-1, not holding) + prices[idx]
)
# skip
best = max(best, recurse(idx+1, actions, holding))
memo[(idx, actions, holding)] = best
return best
return recurse(0, 4, False)clever one-pass simulation
There’s four values that we need to keep track of, and we can simulate through and keep the best possible for every value.
# O(n)
def maxProfit(self, prices: List[int]) -> int:
firstTransactionCost = prices[0]
firstTransactionProfit = 0
mostMoneyInPocket = -prices[0]
profitFromTwoTransactions = 0
for currentPrice in prices:
firstTransactionCost = min(firstTransactionCost, currentPrice)
firstTransactionProfit = max(
firstTransactionProfit, currentPrice-firstTransactionCost
)
mostMoneyInPocket = max(
mostMoneyInPocket, firstTransactionProfit-currentPrice
)
profitFromTwoTransactions = max(
profitFromTwoTransactions, mostMoneyInPocket+currentPrice
)
return profitFromTwoTransactions