Input: k =2, prices =[2,4,1]Output:
2Explanation: Buy on day 1(price =2) and sell on day 2(price =4), profit =4-2=2.
Example 2:
1
2
3
4
5
Input:
k =2, prices =[3,2,6,5,0,3]Output:
7Explanation: Buy on day 2(price =2) and sell on day 3(price =6), profit =6-2=4. Then buy on day 5(price =0) and sell on day 6(price =3), profit =3-0=3.
classSolution {
publicintmaxProfit(int k, int[] prices) {
return dfs(k, prices.length, prices);
}
publicintdfs(int k, int n, int[] prices) {
if (k == 0 || n == 0)
return 0; // if any of then becomes 0, no profitint curr = prices[n - 1], profit = 0;
// Finding max profit for current day. As we are moving backwards, if we// sell on day let say x, and purchase it on y (where y<x) then the next// possible transaction would be from day y. That's why we use function// solve() again while getting our profit. Also as we have transacted// once, we reduce the value of k.for (int i = n - 1; i >= 0; i--) {
profit = Math.max(profit, (curr - prices[i]) + dfs(k - 1, i, prices));
}
// if we decide not to transact on that day, we simply reduce n, else go// with our 'profit'.return Math.max(dfs(k, n - 1, prices), profit);
}
}
classSolution {
publicintmaxProfit(int k, int[] prices) {
int n = prices.length;
int[][] dp =newint[k + 1][n + 1];
for (int[] row : dp) {
Arrays.fill(row, -1);
}
return dfs(k, n, prices, dp);
}
publicintdfs(int k, int n, int[] prices, int[][] dp) {
if (k == 0 || n == 0) {
return 0; // No profit if either k or n is zero }
if (dp[k][n]!=-1) {
return dp[k][n];
}
int curr = prices[n - 1];
int maxProfit = 0;
// Calculate max profit for the current day.// By moving backwards and selling on day p, buying on day q (where q <// p), the next transaction would start from day q. Each time we// transact, k is decreased by 1.for (int i = n - 1; i >= 0; i--) {
maxProfit = Math.max(
maxProfit, (curr - prices[i]) + dfs(k - 1, i, prices, dp));
}
// If no transaction on the current day, proceed by reducing n;// otherwise, use the computed maxProfit. dp[k][n]= Math.max(dfs(k, n - 1, prices, dp), maxProfit);
return dp[k][n];
}
}
Let’s create a dp array of size [k+1][n]. For any t in [1, k] and i in [1, n):
Let dp[t][i] denote the maximum profit using at most t transactions up to day i (inclusive). The relation can be expressed as:
1
2
3
4
5
6
dp[t][i]= max(dp[t][i-1], max(prices[i]- prices[j]+ dp[t-1][j]))for all j in the range [0, i-1]dp[t][i-1]-> If no transaction is made on day i
dp[t-1][j]-> Best profit possible with t-1 transactions on day j
To summarize, dp[t][i] is the maximum of:
dp[t][i-1], representing the scenario where no transaction is made on the ith day.
The maximum profit from selling on the ith day. To sell shares on the ith day, they must be bought on any day in the range [0, i-1]. If shares are bought on the jth day and sold on the ith day, the maximum profit is prices[i] - prices[j] + dp[t-1][j], where j varies from 0 to i-1. Here, dp[t-1][j] represents the best we could have done with one less transaction by the jth day.
The previous solution has a time complexity of O(k * n^2). However, this can be optimized to calculate the maximum profit from selling shares on the ith day in constant time.
Consider the previous DP relation:
1
2
dp[t][i]= max(profit [t][i-1], max(prices[i]– prices[j]+ dp[t-1][j]))for all j in range [0, i-1]
Notice that:
1
2
max(prices[i]– prices[j]+ dp[t-1][j])for all j in range [0, i-1]
can be rewritten as:
1
2
3
4
5
6
7
8
= prices[i]+ max(dp[t-1][j]– prices[j])for all j in range [0, i-1]= prices[i]+ max(prevDiff, dp[t-1][i-1]– prices[i-1])where prevDiff ismax(dp[t-1][j]– prices[j])for all j in range [0, i-2]
If we’ve already computed max(dp[t-1][j] - prices[j]) for all j in the range [0, i-2], then we can calculate it for j = i-1 in constant time. This eliminates the need to look back through the range [0, i-1] to find the best day to buy. Instead, we can determine it in constant time using this revised relation:
1
2
dp[t][i]= max(dp[t][i-1], prices[i]+ max(prevDiff, dp[t-1][i-1]- prices[i-1]))where prevDiff ismax(dp[t-1][j]- prices[j])for all j in the range [0, i-2]
dp[t][i-1]: If no transaction is made on the ith day
prices[i] - prices[j] + dp[t-1][j]: If a transaction is made on the ith day, compute maxDiff for the range [0, i-1]. This reuses the previously computed maxDiff, reducing the time complexity to O(n).
The idea is to design a state machine that accurately represents the problem. There are three possible actions:
Rest: Do nothing
Buy
Sell
When no stock has been bought, we are in a rest state. From here, we can either remain resting or buy a stock. If we own stock, we can either rest or sell it.
Here is the state machine for 2 transactions (k=2):
Understanding the state diagram for k = 2:
We start at state 0, where we can either rest (do nothing) or buy stock at the current price.
Resting keeps us in state 0.
Buying moves us to state 1 and deducts the stock price from our funds.
In state 1, we can either rest or sell our stock.
Resting keeps us in state 1.
Selling earns us money (stock price of the day) and moves us to state 2.
This completes one transaction. With k-1 transactions remaining, we move to the next steps.
In state 2, we can again choose to rest or buy more stock.
Resting keeps us in state 2.
Buying moves us to state 3.
In state 3, the options are once again to rest or make the final sale.
Resting keeps us in state 3.
Selling completes our transactions and moves us to the final state 4.
Translating the state diagram into code
1
2
3
4
5
6
7
8
// Assuming we are in state S
// For buying, we spend money; alternatively, we can do nothing
// Doing nothing: S->S
// Buying: X->S, with a cost
S = max(S, X-prices[i])// For selling a stock, the situation is similar
S = max(S, X+prices[i])
For generic k:
For k = 2 transactions, there are 4 states. Similarly, for k transactions, there are 2k possible states.
Method 7 - Using State Machine and Recursion + Memo#
This problem resembles the knapsack problem with three variables: current index, remaining transactions, and whether a stock is already bought. Based on these variables, we have three possible actions:
Do nothing.
If a stock is already bought, sell it.
Buy the current stock and ignore the previously bought stock.
Base condition: if the index is out of range or the transaction limit is reached.