Best Time to Buy and Sell Stock V
MEDIUMDescription
You are given an integer array prices where prices[i] is the price of a stock in dollars on the ith day, and an integer k.
You are allowed to make at most k transactions, where each transaction can be either of the following:
-
Normal transaction: Buy on day
i, then sell on a later dayjwherei < j. You profitprices[j] - prices[i]. -
Short selling transaction: Sell on day
i, then buy back on a later dayjwherei < j. You profitprices[i] - prices[j].
Note that you must complete each transaction before starting another. Additionally, you can't buy or sell on the same day you are selling or buying back as part of a previous transaction.
Return the maximum total profit you can earn by making at most k transactions.
Example 1:
Input: prices = [1,7,9,8,2], k = 2
Output: 14
Explanation:
We can make $14 of profit through 2 transactions:- A normal transaction: buy the stock on day 0 for $1 then sell it on day 2 for $9.
- A short selling transaction: sell the stock on day 3 for $8 then buy back on day 4 for $2.
Example 2:
Input: prices = [12,16,19,19,8,1,19,13,9], k = 3
Output: 36
Explanation:
We can make $36 of profit through 3 transactions:- A normal transaction: buy the stock on day 0 for $12 then sell it on day 2 for $19.
- A short selling transaction: sell the stock on day 3 for $19 then buy back on day 4 for $8.
- A normal transaction: buy the stock on day 5 for $1 then sell it on day 6 for $19.
Constraints:
2 <= prices.length <= 1031 <= prices[i] <= 1091 <= k <= prices.length / 2
Approaches
Checkout 2 different approaches to solve Best Time to Buy and Sell Stock V. Click on different approaches to view the approach and algorithm in detail.
Recursive Approach with Memoization
This approach uses recursion with memoization, which is a form of top-down dynamic programming. We define a function that calculates the maximum profit from a given day with a certain number of transactions remaining. The state is defined by (current_day, transactions_left). For each state, we explore all possible future transactions, leading to a high time complexity.
Algorithm
- Define a recursive function
solve(i, k)that computes the maximum profit from dayiton-1with at mostktransactions. - The base cases for the recursion are:
- If
kis 0, no more transactions can be made, so the profit is 0. - If
iis greater than or equal ton-1, there are not enough days to make a transaction, so the profit is 0.
- If
- In the recursive step for
solve(i, k), we consider two main possibilities:- Skip day
i: We don't start a transaction on dayi. The maximum profit is then found by solving the subproblem for the remaining days, which issolve(i + 1, k). - Start a transaction on day
i: We can pair dayiwith any subsequent dayj(wherej > i) to form a transaction. The profit for this single transaction is|prices[j] - prices[i]|. After this transaction, which ends on dayj, we can start the next transaction from dayj+1. The profit from the remainingk-1transactions issolve(j + 1, k - 1). We iterate through all possiblejfromi+1ton-1and take the one that maximizes the total profit.
- Skip day
- The result for
solve(i, k)is the maximum of the outcomes from these two possibilities. - To avoid recomputing the same subproblems, we use a 2D array
memo[i][k]for memoization.
We can define a DP state dp[i][k] as the maximum profit achievable using the subarray of prices from day i to the end, with at most k transactions allowed.
The goal is to compute dp[0][k].
The recurrence relation can be formulated as follows:
dp[i][k] = max(option1, option2)
-
Option 1: Don't transact starting at day
iWe skip dayiand the problem reduces to finding the max profit from dayi+1onwards withktransactions. The profit isdp[i+1][k]. -
Option 2: Start a transaction at day
iWe start a transaction at dayiand end it at some future dayj > i. The profit from this transaction is|prices[j] - prices[i]|. After this transaction concludes on dayj, we are left withk-1transactions and can continue from dayj+1. We must try every possiblejfromi+1ton-1and choose the one that yields the maximum total profit. This is expressed as:max_{j=i+1}^{n-1} (|prices[j] - prices[i]| + dp[j+1][k-1])
Combining these, the full recurrence is:
dp[i][k] = max(dp[i+1][k], max_{j=i+1}^{n-1} (|prices[j] - prices[i]| + dp[j+1][k-1]))
This can be implemented using a recursive function with a 2D memoization table to store the results of dp[i][k] and avoid redundant calculations.
import java.util.Arrays;
class Solution {
private long[][] memo;
private int[] prices;
private int n;
public long maxProfit(int[] prices, int k) {
this.prices = prices;
this.n = prices.length;
this.memo = new long[n][k + 1];
for (long[] row : memo) {
Arrays.fill(row, -1);
}
return solve(0, k);
}
private long solve(int i, int k) {
if (k == 0 || i >= n - 1) {
return 0;
}
if (memo[i][k] != -1) {
return memo[i][k];
}
// Option 1: Skip day i
long res = solve(i + 1, k);
// Option 2: Start a transaction on day i
for (int j = i + 1; j < n; j++) {
long currentProfit = Math.abs((long)prices[j] - prices[i]);
long futureProfit = (j + 1 < n) ? solve(j + 1, k - 1) : 0;
res = Math.max(res, currentProfit + futureProfit);
}
return memo[i][k] = res;
}
}
Complexity Analysis
Pros and Cons
- The logic is a direct translation of the problem statement, making it relatively straightforward to understand.
- It correctly models the sequential, non-overlapping nature of transactions.
- The time complexity of O(k * n^2) is too slow for the given constraints and will likely result in a 'Time Limit Exceeded' error.
Video Solution
Watch the video walkthrough for Best Time to Buy and Sell Stock V
Similar Questions
5 related questions you might find useful
Patterns:
Data Structures:
Subscribe to Scale Engineer newsletter
Learn about System Design, Software Engineering, and interview experiences every week.
No spam, unsubscribe at any time.