Given an array, arr, of n integers, where arr[i] represents the price of the stock on an ith day, determine the maximum profit achievable by completing at most two transactions in total.
Holding at most one share of the stock at any time is allowed, meaning buying and selling the stock twice is permitted, but the stock must be sold before buying it again. Buying and selling the stock on the same day is allowed.
Input: arr = [4, 2, 7, 1, 11, 5]
Output: 15
Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 7), profit = 7 - 2 = 5. Then buy on day 4 (price = 1) and sell on day 5 (price = 11), profit = 11 - 1 = 10. Total profit is 5 + 10 = 15.
Input: arr = [1, 3, 2, 8, 4, 9]
Output: 12
Explanation: Buy on day 1 (price = 1) and sell on day 4 (price = 8), profit = 8 - 1 = 7. Then buy on day 5 (price = 4) and sell on day 6 (price = 9), profit = 9 - 4 = 5. Total profit is 7 + 5 = 12.
Input: arr = [5, 7, 2, 10, 6, 9]
Upon observation, we find that every day, there will be two choices, either to do nothing and move to the next day or to buy/sell (based on the last transaction and the number of transactions left) and find out the profit. Therefore we need to generate all the choices in order to compare the profit. As all possible choices needs to be generated, we will use recursion.
f(ind, buy, cap) will give the maximum profit that can be generated from day 'ind' to day (n-1), where 'buy' tells that whether we need to buy or sell the stock at day 'ind' and 'cap' tells the number of transactions left.
Option 1: To do no transaction and move to the next day. In this case, the net profit earned will be 0 from the current transaction, and to calculate the maximum profit starting from the next day, we will recursively call f(ind+1, 0, cap). As we have not bought the stock, the ‘buy’ variable value will still remain 0, indicating that stock can be bought on next day. And the ‘cap’ variable will remain the same as if no transaction took place.
Option 2: The other option is to buy the stock on the current day. In this case, the net profit earned from the current transaction will be -Arr[i]. As we are buying the stock, we are giving money out of our pocket, therefore the profit we earn is negative. To calculate the maximum profit starting from the next day, recursively call f(ind+1,1, cap). As the stock has been bought, the ‘buy’ variable value will change to 1, indicating that we can’t buy and only sell the stock the next day. As we have only bought the stock and not sold it the transaction remains incomplete and the ‘cap’ variable value remains unchanged.
Option 1: To do no transaction and move to the next day. In this case, the net profit earned will be 0 from the current transaction, and to calculate the maximum profit starting from the next day, we will recursively call f(ind+1,1, cap). As we have not bought the stock, the ‘buy’ variable value will still remain at 1, indicating that we can’t buy and only sell the stock the next day. And the ‘cap’ variable will remain the same as if no transaction took place.
Option 2: The other option is to sell the stock on the current day. In this case, the net profit earned from the current transaction will be +Arr[i]. As we are selling the stock, we are putting the money into our pocket, therefore the profit we earn is positive. To calculate the maximum profit starting from the next day, we will recursively call f(ind+1,0,cap-1). As we have sold the stock, the ‘buy’ variable value will change to 0, indicating that we can buy the stock the next day. As we have sold the earlier bought stock, we make one complete transaction, therefore now we update the ‘cap’ variable’s value to cap-1.
Note: Buying and selling a stock together counts as one complete transaction.
/*It is pseudocode and it is not tied to
any specific programming language*/
f(ind, buy, cap){
//Base case
if(buy == 0){
op1 = 0 + f(ind + 1, 0, cap)
op2 = -arr[ind] + f(ind + 1, 1, cap)
}
if(buy == 1){
op1 = 0 + f(ind + 1, 1, cap)
op2 = arr[ind] + f(ind + 1, 0, cap-1)
}
}
/*It is pseudocode and it is not tied to
any specific programming language*/
f(ind, buy, cap){
//Base case
if(buy == 0){
op1 = 0 + f(ind + 1, 0, cap)
op2 = -arr[ind] + f(ind + 1, 1, cap)
}
if(buy == 1){
op1 = 0 + f(ind + 1, 1, cap)
op2 = arr[ind] + f(ind + 1, 0, cap-1)
}
return max(op1, op2)
}
#include <bits/stdc++.h>
using namespace std;
class Solution{
private:
//Function to find the maximum profit earned using recursion
int func(int ind, int buy, int cap, int n, vector<int> &arr) {
// Base case
if (ind == n || cap == 0) {
return 0;
}
int profit = 0;
// We can buy the stock
if (buy == 0) {
profit = max(0 + func(ind + 1, 0, cap, n, arr), (-1)*arr[ind] + func(ind + 1, 1, cap, n, arr));
}
//We can sell the stock
if (buy == 1) {
profit = max(0 + func(ind + 1, 1, cap, n, arr), arr[ind] + func(ind + 1, 0, cap-1, n, arr));
}
// Return the calculated profit
return profit;
}
public:
//Function to calculate the maximum profit earned
int stockBuySell(vector<int> &arr, int n) {
if (n == 0)
return 0;
int ans = func(0, 0, 2, n, arr);
//Return the maximum profit
return ans;
}
};
int main() {
int n = 8;
vector<int> arr = {3, 3, 5, 0, 0, 3, 1, 4};
//Create an instance of Solution class
Solution sol;
// Call the stockBuySell function and print the result
cout << "The maximum profit that can be generated is " << sol.stockBuySell(arr, n);
return 0;
}
import java.util.*;
class Solution {
// Function to find the maximum profit earned using recursion
private int func(int ind, int buy, int cap, int n, int[] arr) {
// Base case
if (ind == n || cap == 0) {
return 0;
}
int profit = 0;
// We can buy the stock
if (buy == 0) {
profit = Math.max(0 + func(ind + 1, 0, cap, n, arr), (-1)*arr[ind] + func(ind + 1, 1, cap, n, arr));
}
// We can sell the stock
if (buy == 1) {
profit = Math.max(0 + func(ind + 1, 1, cap, n, arr), arr[ind] + func(ind + 1, 0, cap-1, n, arr));
}
// Return the calculated profit
return profit;
}
// Function to calculate the maximum profit earned
public int stockBuySell(int[]arr, int n) {
if (n == 0)
return 0;
int ans = func(0, 0, 2, n, arr);
// Return the maximum profit
return ans;
}
public static void main(String[] args) {
int n = 8;
int[] arr = {3, 3, 5, 0, 0, 3, 1, 4};
// Create an instance of Solution class
Solution sol = new Solution();
// Call the stockBuySell function and print the result
System.out.println("The maximum profit that can be generated is " + sol.stockBuySell(arr, n));
}
}
class Solution:
# Function to find the maximum profit earned using recursion
def func(self, ind, buy, cap, n, arr):
# Base case
if(ind == n or cap == 0):
return 0
profit = 0
# We can buy the stock
if buy == 0:
profit = max(0 + self.func(ind + 1, 0, cap, n, arr), (-1)*arr[ind] + self.func(ind + 1, 1, cap, n, arr))
# We can sell the stock
if buy == 1:
profit = max(0 + self.func(ind + 1, 1, cap, n, arr), arr[ind] + self.func(ind + 1, 0, cap-1, n, arr))
# Return the calculated profit
return profit
# Function to calculate the maximum profit earned
def stockBuySell(self, arr, n):
if n == 0:
return 0
ans = self.func(0, 0, 2, n, arr)
# Return the maximum profit
return ans
if __name__ == "__main__":
n = 8
arr = [3, 3, 5, 0, 0, 3, 1, 4]
# Create an instance of Solution class
sol = Solution()
# Call the stockBuySell function and print the result
print("The maximum profit that can be generated is", sol.stockBuySell(arr, n))
class Solution {
// Function to find the maximum profit earned using recursion
func(ind, buy, cap, n, arr) {
// Base case
if (ind === n || cap === 0) {
return 0;
}
let profit = 0;
// We can buy the stock
if (buy === 0) {
profit = Math.max(0 + this.func(ind + 1, 0, cap, n, arr), (-1)*arr[ind] + this.func(ind + 1, 1, cap, n, arr));
}
// We can sell the stock
if (buy === 1) {
profit = Math.max(0 + this.func(ind + 1, 1, cap, n, arr), arr[ind] + this.func(ind + 1, 0, cap-1, n, arr));
}
// Return the calculated profit
return profit;
}
// Function to calculate the maximum profit earned
stockBuySell(arr, n) {
if (n === 0)
return 0;
let ans = this.func(0, 0, 2, n, arr);
// Return the maximum profit
return ans;
}
}
function main() {
const n = 8;
const arr = [3, 3, 5, 0, 0, 3, 1, 4];
// Create an instance of Solution class
const sol = new Solution();
// Call the stockBuySell function and print the result
console.log("The maximum profit that can be generated is", sol.stockBuySell(arr, n));
}
main();
If we draw the recursion tree, we will see that there are overlapping subproblems. Hence the DP approaches can be applied to the recursive solution.
The dp array stores the calculations of subproblems. Initialize dp array with -1, to indicate that no subproblem has been solved yet.
#include <bits/stdc++.h>
using namespace std;
class Solution{
private:
//Function to find the maximum profit earned using memoization
int func(int ind, int buy, int cap, int n, vector<int> &arr, vector<vector<vector<int>>>& dp) {
// Base case
if (ind == n || cap == 0) {
return 0;
}
/* If the result for this state has
already been calculated, return it*/
if (dp[ind][buy][cap] != -1) {
return dp[ind][buy][cap];
}
int profit = 0;
// We can buy the stock
if (buy == 0) {
profit = max(0 + func(ind + 1, 0, cap, n, arr, dp), (-1)*arr[ind] + func(ind + 1, 1, cap, n, arr, dp));
}
//We can sell the stock
if (buy == 1) {
profit = max(0 + func(ind + 1, 1, cap, n, arr, dp), arr[ind] + func(ind + 1, 0, cap-1, n, arr, dp));
}
/* Store the value in dp array and
return the calculated profit */
return dp[ind][buy][cap] = profit;
}
public:
//Function to calculate the maximum profit earned
int stockBuySell(vector<int> &arr, int n) {
if (n == 0)
return 0;
// Initialize a DP table to memoize results
vector<vector<vector<int>>> dp(n, vector<vector<int>>(2, vector<int>(3, -1)));
int ans = func(0, 0, 2, n, arr, dp);
//Return the maximum profit
return ans;
}
};
int main() {
int n = 8;
vector<int> arr = {3, 3, 5, 0, 0, 3, 1, 4};
//Create an instance of Solution class
Solution sol;
// Call the stockBuySell function and print the result
cout << "The maximum profit that can be generated is " << sol.stockBuySell(arr, n);
return 0;
}
import java.util.*;
class Solution {
// Function to find the maximum profit earned using memoization
private int func(int ind, int buy, int cap, int n, int[] arr, int[][][] dp) {
// Base case
if (ind == n || cap == 0) {
return 0;
}
/* If the result for this state has
already been calculated, return it*/
if (dp[ind][buy][cap] != -1) {
return dp[ind][buy][cap];
}
int profit = 0;
// We can buy the stock
if (buy == 0) {
profit = Math.max(0 + func(ind + 1, 0, cap, n, arr, dp), (-1)*arr[ind] + func(ind + 1, 1, cap, n, arr, dp));
}
// We can sell the stock
if (buy == 1) {
profit = Math.max(0 + func(ind + 1, 1, cap, n, arr, dp), arr[ind] + func(ind + 1, 0, cap-1, n, arr, dp));
}
/* Store the value in dp array and
return the calculated profit */
return dp[ind][buy][cap] = profit;
}
// Function to calculate the maximum profit earned
public int stockBuySell(int[] arr, int n) {
if (n == 0)
return 0;
// Declare a DP table to memoize results
int[][][] dp = new int[n][2][3];
// Initialize the dp array with -1
for (int i = 0; i < n; i++) {
for (int j = 0; j < 2; j++) {
Arrays.fill(dp[i][j], -1);
}
}
int ans = func(0, 0, 2, n, arr, dp);
// Return the maximum profit
return ans;
}
public static void main(String[] args) {
int n = 8;
int[] arr = {3, 3, 5, 0, 0, 3, 1, 4};
// Create an instance of Solution class
Solution sol = new Solution();
// Call the stockBuySell function and print the result
System.out.println("The maximum profit that can be generated is " + sol.stockBuySell(arr, n));
}
}
class Solution:
# Function to find maximum profit earned using memoization
def func(self, ind, buy, cap, n, arr, dp):
# Base case
if ind == n or cap == 0:
return 0
""" If the result for this state has
already been calculated, return it"""
if dp[ind][buy][cap] != -1:
return dp[ind][buy][cap]
profit = 0
# We can buy the stock
if buy == 0:
profit = max(0 + self.func(ind + 1, 0, cap, n, arr, dp), (-1)*arr[ind] + self.func(ind + 1, 1, cap, n, arr, dp))
# We can sell the stock
if buy == 1:
profit = max(0 + self.func(ind + 1, 1, cap, n, arr, dp), arr[ind] + self.func(ind + 1, 0, cap-1, n, arr, dp))
""" Store the value in dp array and
return the calculated profit"""
dp[ind][buy][cap] = profit
return dp[ind][buy][cap]
# Function to calculate the maximum profit earned
def stockBuySell(self, arr, n):
if n == 0:
return 0
""" Declare a 3D DP table with dimensions
(n) x 2 x 3 and initialize it with -1 """
dp = [[[-1 for _ in range(3)] for _ in range(2)] for _ in range(n)]
ans = self.func(0, 0, 2, n, arr, dp)
# Return the maximum profit
return ans
if __name__ == "__main__":
n = 8
arr = [3, 3, 5, 0, 0, 3, 1, 4]
# Create an instance of Solution class
sol = Solution()
# Call the stockBuySell function and print the result
print("The maximum profit that can be generated is", sol.stockBuySell(arr, n))
class Solution {
// Function to find maximum profit earned using memoization
func(ind, buy, cap, n, arr, dp) {
// Base case
if (ind === n || cap === 0) {
return 0;
}
/* If the result for this state has
already been calculated, return it*/
if (dp[ind][buy][cap] !== -1) {
return dp[ind][buy][cap];
}
let profit = 0;
// We can buy the stock
if (buy === 0) {
profit = Math.max(0 + this.func(ind + 1, 0, cap, n, arr, dp), (-1)*arr[ind] + this.func(ind + 1, 1, cap, n, arr, dp));
}
// We can sell the stock
if (buy === 1) {
profit = Math.max(0 + this.func(ind + 1, 1, cap, n, arr, dp), arr[ind] + this.func(ind + 1, 0, cap-1, n, arr, dp));
}
/* Store the value in dp array and
return the calculated profit */
dp[ind][buy][cap] = profit;
return dp[ind][buy][cap];
}
// Function to calculate the maximum profit earned
stockBuySell(arr, n) {
if (n === 0)
return 0;
// Initialize a DP table to memoize results
const dp = new Array(n).fill(null).map(() =>
new Array(2).fill(null).map(() =>
new Array(3).fill(-1)
)
);
let ans = this.func(0, 0, 2, n, arr, dp);
// Return the maximum profit
return ans;
}
}
const n = 8;
const arr = [3, 3, 5, 0, 0, 3, 1, 4];
// Create an instance of Solution class
const sol = new Solution();
// Call the stockBuySell function and print the result
console.log("The maximum profit that can be generated is", sol.stockBuySell(arr, n));
In order to convert a recursive code to tabulation code, we try to convert the recursive code to tabulation and here are the steps:
When cap == 0, the other two variables, 'ind' and 'cap' can take any value, therefore we set dp[ind][buy][0] = 0 usnig nested loops.
#include <bits/stdc++.h>
using namespace std;
class Solution {
private:
//Function to find the maximum profit using tabulation
int func(vector<int>& arr, int n) {
/* Creating a 3D DP array of size
[n+1][2][3] initialized to 0*/
vector<vector<vector<int>>> dp(n + 1, vector<vector<int>>(2, vector<int>(3, 0)));
/* Base case: dp array is already initialized
to 0, covering the base case.*/
// Iterate backwards through the prices array
for (int ind = n - 1; ind >= 0; ind--) {
// buy can be 0 (buying) or 1 (selling)
for (int buy = 0; buy <= 1; buy++) {
/* cap represents the number of
transactions completed (can be 1 or 2)*/
for (int cap = 1; cap <= 2; cap++) {
// We can buy the stock
if (buy == 0) {
dp[ind][buy][cap] = max(0 + dp[ind + 1][0][cap],
(-1)*arr[ind] + dp[ind + 1][1][cap]);
}
// We can sell the stock
if (buy == 1) {
dp[ind][buy][cap] = max(0 + dp[ind + 1][1][cap],
arr[ind] + dp[ind + 1][0][cap - 1]);
}
}
}
}
/* The result is stored in dp[0][0][2] which
represents maximum profit after final transaction.*/
return dp[0][0][2];
}
public:
//Function to find the maximum profit
int stockBuySell(vector<int> &arr, int n){
//Return the answer
return func(arr, n);
}
};
int main() {
vector<int> prices = {3, 3, 5, 0, 0, 3, 1, 4};
int n = prices.size();
// Create an instance of the Solution class
Solution sol;
// Call the function and print the result
cout << "The maximum profit that can be generated is " << sol.stockBuySell(prices, n) << endl;
return 0;
}
import java.util.*;
class Solution {
// Function to find the maximum profit using tabulation
private int func(int[] arr, int n) {
/* Declaring a 3D DP array of
size [n+1][2][3] initialized to 0*/
int[][][] dp = new int[n + 1][2][3];
/* Base case: dp array is already i
nitialized to 0, covering the base case.*/
// Iterate backwards through the prices array
for (int ind = n - 1; ind >= 0; ind--) {
// buy can be 0 (buying) or 1 (selling)
for (int buy = 0; buy <= 1; buy++) {
/* cap represents the number of
transactions completed (can be 1 or 2)*/
for (int cap = 1; cap <= 2; cap++) {
// We can buy the stock
if (buy == 0) {
dp[ind][buy][cap] = Math.max(0 + dp[ind + 1][0][cap],
(-1)*arr[ind] + dp[ind + 1][1][cap]);
}
// We can sell the stock
if (buy == 1) {
dp[ind][buy][cap] = Math.max(0 + dp[ind + 1][1][cap],
arr[ind] + dp[ind + 1][0][cap - 1]);
}
}
}
}
/* The result is stored in dp[0][0][2] which
represents maximum profit after the final transaction.*/
return dp[0][0][2];
}
// Function to find the maximum profit
public int stockBuySell(int[] arr, int n) {
// Return the answer
return func(arr, n);
}
public static void main(String[] args) {
int[] prices = {3, 3, 5, 0, 0, 3, 1, 4};
int n = prices.length;
// Create an instance of the Solution class
Solution sol = new Solution();
// Call the function and print the result
System.out.println("The maximum profit that can be generated is " + sol.stockBuySell(prices, n));
}
}
class Solution:
# Function to find the maximum profit using tabulation
def func(self, arr, n):
""" Declaring a 3D DP array of size
[n+1][2][3] initialized to 0"""
dp = [[[0 for _ in range(3)] for _ in range(2)] for _ in range(n + 1)]
""" Base case: dp array is already
initialized to 0, covering the base case."""
# Iterate backwards through the prices array
for ind in range(n - 1, -1, -1):
# buy can be 0 (buying) or 1 (selling)
for buy in range(2):
""" cap represents the number of
transactions completed (can be 1 or 2)"""
for cap in range(1, 3):
# We can buy the stock
if buy == 0:
dp[ind][buy][cap] = max(0 + dp[ind + 1][0][cap],
(-1)*arr[ind] + dp[ind + 1][1][cap])
# We can sell the stock
if buy == 1:
dp[ind][buy][cap] = max(0 + dp[ind + 1][1][cap],
arr[ind] + dp[ind + 1][0][cap - 1])
""" The result is stored in dp[0][0][2] which
represents maximum profit after the final transaction."""
return dp[0][0][2]
# Function to find the maximum profit
def stockBuySell(self, arr, n):
# Return the answer
return self.func(arr, n)
if __name__ == "__main__":
prices = [3, 3, 5, 0, 0, 3, 1, 4]
n = len(prices)
# Create an instance of the Solution class
sol = Solution()
# Call the function and print the result
print("The maximum profit that can be generated is", sol.stockBuySell(prices, n))
class Solution {
// Function to find the maximum profit using tabulation
func(arr, n) {
/* Declaring a 3D DP array of size
[n+1][2][3] initialized to 0*/
let dp = new Array(n + 1).fill().map(() => new Array(2).fill().map(() => new Array(3).fill(0)));
/* Base case: dp array is already
initialized to 0, covering the base case.*/
// Iterate backwards through the prices array
for (let ind = n - 1; ind >= 0; ind--) {
// buy can be 0 (buying) or 1 (selling)
for (let buy = 0; buy <= 1; buy++) {
/* cap represents the number of
transactions completed (can be 1 or 2)*/
for (let cap = 1; cap <= 2; cap++) {
// We can buy the stock
if (buy === 0) {
dp[ind][buy][cap] = Math.max(0 + dp[ind + 1][0][cap],
(-1)*arr[ind] + dp[ind + 1][1][cap]);
}
// We can sell the stock
if (buy === 1) {
dp[ind][buy][cap] = Math.max(0 + dp[ind + 1][1][cap],
arr[ind] + dp[ind + 1][0][cap - 1]);
}
}
}
}
/* The result is stored in dp[0][0][2] which
represents maximum profit after the final transaction.*/
return dp[0][0][2];
}
// Function to find the maximum profit
stockBuySell(arr, n) {
// Return the answer
return this.func(arr, n);
}
}
let prices = [3, 3, 5, 0, 0, 3, 1, 4];
let n = prices.length;
// Create an instance of the Solution class
let sol = new Solution();
// Call the function and print the result
console.log("The maximum profit that can be generated is " + sol.stockBuySell(prices, n));
If we observe the relation obtained in the tabulation, dp[ind][buy][cap] = max( dp[ind+1][buy][cap] , max( dp[ind+1][!buy][cap]), We see that to calculate a value of a cell of the dp array, we need only the next row values(say ahead of ind+1). So, we don’t need to store an entire 2-D array. Hence we can space optimize it.
#include <bits/stdc++.h>
using namespace std;
class Solution {
private:
/*Function to find our maximum
profit using space optimization*/
int func(vector<int>& arr, int n) {
/* Create two 2D arrays to store profit information,
one for current state and one for the ahead state.*/
vector<vector<int>> ahead(2, vector<int>(3, 0));
vector<vector<int>> cur(2, vector<int>(3, 0));
// Iterate backwards through the prices array
for (int ind = n - 1; ind >= 0; ind--) {
// buy can be 0 (buying) or 1 (selling)
for (int buy = 0; buy <= 1; buy++) {
/* cap represents the number of
transactions completed (can be 1 or 2)*/
for (int cap = 1; cap <= 2; cap++) {
// We can buy the stock
if (buy == 0) {
cur[buy][cap] = max(0 + ahead[0][cap],
-arr[ind] + ahead[1][cap]);
}
// We can sell the stock
if (buy == 1) {
cur[buy][cap] = max(0 + ahead[1][cap],
arr[ind] + ahead[0][cap - 1]);
}
}
}
/* Update the ahead state with the
current state for the next iteration*/
ahead = cur;
}
/* Return the maximum profit after completing
2 transactions starting from index 0.*/
return ahead[0][2];
}
public:
//Function to find out maximum profit
int stockBuySell(vector<int> &arr, int n){
//Return the answer
return func(arr, n);
}
};
int main() {
vector<int> prices = {3, 3, 5, 0, 0, 3, 1, 4};
int n = prices.size();
// Create an instance of the Solution class
Solution sol;
// Call the funntion and print the result
cout << "The maximum profit that can be generated is " << sol.stockBuySell(prices, n) << endl;
return 0;
}
import java.util.*;
class Solution {
/* Function to find our maximum
profit using space optimization*/
private int func(int[] arr, int n) {
/* Create two 2D arrays to store profit information,
one for current state and one for the ahead state.*/
int[][] ahead = new int[2][3];
int[][] cur = new int[2][3];
// Iterate backwards through the prices array
for (int ind = n - 1; ind >= 0; ind--) {
// buy can be 0 (buying) or 1 (selling)
for (int buy = 0; buy <= 1; buy++) {
/* cap represents the number of
transactions completed (can be 1 or 2)*/
for (int cap = 1; cap <= 2; cap++) {
// We can buy the stock
if (buy == 0) {
cur[buy][cap] = Math.max(0 + ahead[0][cap],
(-1)*arr[ind] + ahead[1][cap]);
}
// We can sell the stock
if (buy == 1) {
cur[buy][cap] = Math.max(0 + ahead[1][cap],
arr[ind] + ahead[0][cap - 1]);
}
}
}
/* Update the ahead state with the
current state for the next iteration*/
ahead = cur;
}
/* Return the maximum profit after completing
2 transactions starting from index 0*/
return ahead[0][2];
}
// Function to find out maximum profit
public int stockBuySell(int[] arr, int n) {
// Return the answer
return func(arr, n);
}
public static void main(String[] args) {
int[] prices = {3, 3, 5, 0, 0, 3, 1, 4};
int n = prices.length;
// Create an instance of the Solution class
Solution sol = new Solution();
// Call the function and print the result
System.out.println("The maximum profit that can be generated is " + sol.stockBuySell(prices, n));
}
}
class Solution:
""" Function to find our maximum
profit using space optimization"""
def func(self, arr, n):
""" Create two 2D arrays to store profit information,
one for current state and one for the ahead state."""
ahead = [[0] * 3 for _ in range(2)]
cur = [[0] * 3 for _ in range(2)]
# Iterate backwards through the prices array
for ind in range(n - 1, -1, -1):
# buy can be 0 (buying) or 1 (selling)
for buy in range(2):
""" cap represents the number of
transactions completed (can be 1 or 2)"""
for cap in range(1, 3):
# We can buy the stock
if buy == 0:
cur[buy][cap] = max(0 + ahead[0][cap],
-arr[ind] + ahead[1][cap])
# We can sell the stock
if buy == 1:
cur[buy][cap] = max(0 + ahead[1][cap],
arr[ind] + ahead[0][cap - 1])
""" Update the ahead state with the
current state for the next iteration"""
ahead = [row[:] for row in cur]
""" Return the maximum profit after completing
2 transactions starting from index 0"""
return ahead[0][2]
# Function to find out maximum profit
def stockBuySell(self, arr, n):
# Return the answer
return self.func(arr, n)
if __name__ == "__main__":
prices = [3, 3, 5, 0, 0, 3, 1, 4]
n = len(prices)
# Create an instance of the Solution class
sol = Solution()
# Call the function and print the result
print("The maximum profit that can be generated is", sol.stockBuySell(prices, n))
class Solution {
/* Function to find our maximum
profit using space optimization*/
func(arr, n) {
/* Create two 2D arrays to store profit information,
one for current state and one for the ahead state.*/
let ahead = Array.from({ length: 2 }, () => Array(3).fill(0));
let cur = Array.from({ length: 2 }, () => Array(3).fill(0));
// Iterate backwards through the prices array
for (let ind = n - 1; ind >= 0; ind--) {
// buy can be 0 (buying) or 1 (selling)
for (let buy = 0; buy <= 1; buy++) {
/* cap represents the number of
transactions completed (can be 1 or 2)*/
for (let cap = 1; cap <= 2; cap++) {
// We can buy the stock
if (buy === 0) {
cur[buy][cap] = Math.max(0 + ahead[0][cap],
-arr[ind] + ahead[1][cap]);
}
// We can sell the stock
if (buy == 1) {
cur[buy][cap] = Math.max(0 + ahead[1][cap],
arr[ind] + ahead[0][cap - 1]);
}
}
}
/* Update the ahead state with the
current state for the next iteration*/
ahead = cur;
}
/* Return the maximum profit after completing
2 transactions starting from index 0*/
return ahead[0][2];
}
// Function to find out maximum profit
stockBuySell(arr, n) {
// Return the answer
return this.func(arr, n);
}
}
let prices = [3, 3, 5, 0, 0, 3, 1, 4];
let n = prices.length;
// Create an instance of the Solution class
let sol = new Solution();
// Call the function and print the result
console.log("The maximum profit that can be generated is " + sol.stockBuySell(prices, n));
Q: Why do we need second_buy and second_sell states?
A: Unlike problems where we can trade infinitely, here we are limited to two transactions. Tracking second_buy ensures we properly deduct the cost of the second purchase while maximizing the total profit.
Q: Why don’t we use a DP table for this problem?
A: A DP table (dp[i][k]) could be used, where i represents the day and k represents the number of transactions. However, since k is fixed at 2, we can optimize space to O(1) instead of O(n × k) by using just four variables.
Q: Can we solve this problem using a stack or monotonic queue?
A: A monotonic queue isn’t useful here because transactions depend on past profits rather than just local min/max values. DP efficiently captures the required dependencies.
Q: What if the input was streamed (i.e., prices arrive in real-time instead of an array)?
A: For streaming prices, we must maintain a rolling sliding window approach, updating profits dynamically as new prices arrive.