2907. Maximum Profitable Triplets With Increasing Prices I ๐
Problem Description
You are given two arrays prices
and profits
, both of length n
, where each index i
represents an item in a store with price prices[i]
and profit profits[i]
.
Your task is to select exactly three items with indices i
, j
, and k
that satisfy the following conditions:
- The indices must be in increasing order:
i < j < k
- The prices must be in strictly increasing order:
prices[i] < prices[j] < prices[k]
When you select three items meeting these conditions, your total profit is calculated as profits[i] + profits[j] + profits[k]
.
The goal is to find the maximum possible profit from selecting three items that satisfy both conditions. If it's impossible to find three items meeting the requirements, return -1
.
For example, if you have items where the prices form an increasing sequence at certain positions, you want to pick the three positions that not only have increasing prices but also give you the highest combined profit values.
Intuition
When we need to find three items with specific ordering constraints, a natural approach is to fix one element and search for the other two. Since we need prices[i] < prices[j] < prices[k]
, the middle element j
acts as a pivot point - it must be greater than something on its left and smaller than something on its right.
By fixing the middle element j
, we can break down the problem into two independent subproblems:
- Find the best item to the left of
j
with a smaller price - Find the best item to the right of
j
with a larger price
For each position j
, we want to maximize profits[i] + profits[j] + profits[k]
. Since profits[j]
is fixed when we choose j
, we need to maximize profits[i]
among all valid items on the left and maximize profits[k]
among all valid items on the right.
The key insight is that for each middle position j
:
- We scan all positions
i < j
and track the maximum profit among items withprices[i] < prices[j]
- We scan all positions
k > j
and track the maximum profit among items withprices[k] > prices[j]
If we can find at least one valid item on both sides (meaning both left
and right
are non-zero), we have a valid triplet. We calculate the total profit as left + profits[j] + right
and update our answer if this is better than what we've found so far.
This approach ensures we check all possible middle elements and for each one, we greedily select the best possible left and right elements, guaranteeing we find the optimal solution.
Learn more about Segment Tree patterns.
Solution Approach
The solution implements the enumerate-the-middle-element strategy. Here's how the algorithm works:
-
Initialize the answer: Start with
ans = -1
to handle the case where no valid triplet exists. -
Enumerate the middle element: Loop through each index
j
from0
ton-1
, treatingprofits[j]
as the middle element of our triplet. We storeprofits[j]
asx
for convenience. -
Find the best left element: For each middle position
j
:- Initialize
left = 0
to track the maximum profit on the left - Iterate through all indices
i
from0
toj-1
- For each
i
, check ifprices[i] < prices[j]
- If true and
profits[i] > left
, updateleft = profits[i]
- After this loop,
left
contains the maximum profit among all valid left elements
- Initialize
-
Find the best right element: Similarly for the right side:
- Initialize
right = 0
to track the maximum profit on the right - Iterate through all indices
k
fromj+1
ton-1
- For each
k
, check ifprices[j] < prices[k]
- If true and
profits[k] > right
, updateright = profits[k]
- After this loop,
right
contains the maximum profit among all valid right elements
- Initialize
-
Update the answer: If both
left
andright
are non-zero (meaning we found valid elements on both sides):- Calculate the total profit as
left + x + right
wherex
isprofits[j]
- Update
ans = max(ans, left + x + right)
- Calculate the total profit as
-
Return the result: After checking all possible middle elements, return
ans
.
The time complexity is O(nยฒ)
since for each of the n
middle elements, we scan through potentially all other elements to find the best left and right values. The space complexity is O(1)
as we only use a constant amount of extra space for variables.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to illustrate the solution approach.
Example Input:
prices = [10, 20, 15, 30, 25]
profits = [5, 3, 7, 2, 8]
We need to find three items with indices i < j < k
where prices[i] < prices[j] < prices[k]
.
Step-by-step execution:
When j = 0 (price=10, profit=5):
- Left side: No elements exist before index 0, so
left = 0
- Right side: Check indices 1-4
- Index 1: price=20 > 10 โ, profit=3, so
right = 3
- Index 2: price=15 > 10 โ, profit=7 > 3, so
right = 7
- Index 3: price=30 > 10 โ, profit=2 < 7, keep
right = 7
- Index 4: price=25 > 10 โ, profit=8 > 7, so
right = 8
- Index 1: price=20 > 10 โ, profit=3, so
- Since
left = 0
, no valid triplet exists with j=0 as middle
When j = 1 (price=20, profit=3):
- Left side: Check index 0
- Index 0: price=10 < 20 โ, profit=5, so
left = 5
- Index 0: price=10 < 20 โ, profit=5, so
- Right side: Check indices 2-4
- Index 2: price=15 < 20 โ, skip
- Index 3: price=30 > 20 โ, profit=2, so
right = 2
- Index 4: price=25 > 20 โ, profit=8 > 2, so
right = 8
- Valid triplet found! Total = 5 + 3 + 8 = 16
- Update
ans = 16
When j = 2 (price=15, profit=7):
- Left side: Check indices 0-1
- Index 0: price=10 < 15 โ, profit=5, so
left = 5
- Index 1: price=20 > 15 โ, skip
- Index 0: price=10 < 15 โ, profit=5, so
- Right side: Check indices 3-4
- Index 3: price=30 > 15 โ, profit=2, so
right = 2
- Index 4: price=25 > 15 โ, profit=8 > 2, so
right = 8
- Index 3: price=30 > 15 โ, profit=2, so
- Valid triplet found! Total = 5 + 7 + 8 = 20
- Update
ans = 20
When j = 3 (price=30, profit=2):
- Left side: Check indices 0-2
- Index 0: price=10 < 30 โ, profit=5, so
left = 5
- Index 1: price=20 < 30 โ, profit=3 < 5, keep
left = 5
- Index 2: price=15 < 30 โ, profit=7 > 5, so
left = 7
- Index 0: price=10 < 30 โ, profit=5, so
- Right side: Check index 4
- Index 4: price=25 < 30 โ, skip
- Right side has no valid elements, so
right = 0
- No valid triplet with j=3 as middle
When j = 4 (price=25, profit=8):
- Left side: Check indices 0-3
- Index 0: price=10 < 25 โ, profit=5, so
left = 5
- Index 1: price=20 < 25 โ, profit=3 < 5, keep
left = 5
- Index 2: price=15 < 25 โ, profit=7 > 5, so
left = 7
- Index 3: price=30 > 25 โ, skip
- Index 0: price=10 < 25 โ, profit=5, so
- Right side: No elements after index 4, so
right = 0
- No valid triplet with j=4 as middle
Final Result: The maximum profit is 20, achieved by selecting items at indices (0, 2, 4) with prices (10, 15, 25) and profits (5, 7, 8).
Solution Implementation
1class Solution:
2 def maxProfit(self, prices: List[int], profits: List[int]) -> int:
3 """
4 Find the maximum profit sum from three indices i < j < k
5 where prices[i] < prices[j] < prices[k].
6
7 Args:
8 prices: List of prices at each index
9 profits: List of profits at each index
10
11 Returns:
12 Maximum sum of profits[i] + profits[j] + profits[k] if valid triplet exists,
13 otherwise -1
14 """
15 n = len(prices)
16 max_profit_sum = -1
17
18 # Iterate through each potential middle element j
19 for j, current_profit in enumerate(profits):
20 max_left_profit = 0 # Maximum profit from left side where price[i] < price[j]
21 max_right_profit = 0 # Maximum profit from right side where price[j] < price[k]
22
23 # Find the maximum profit on the left side (i < j)
24 # where prices[i] < prices[j]
25 for i in range(j):
26 if prices[i] < prices[j] and max_left_profit < profits[i]:
27 max_left_profit = profits[i]
28
29 # Find the maximum profit on the right side (k > j)
30 # where prices[j] < prices[k]
31 for k in range(j + 1, n):
32 if prices[j] < prices[k] and max_right_profit < profits[k]:
33 max_right_profit = profits[k]
34
35 # If valid triplet exists (both left and right profits are non-zero),
36 # update the maximum profit sum
37 if max_left_profit and max_right_profit:
38 max_profit_sum = max(max_profit_sum,
39 max_left_profit + current_profit + max_right_profit)
40
41 return max_profit_sum
42
1class Solution {
2 /**
3 * Finds the maximum profit from selecting three items where prices are strictly increasing.
4 * For valid triplet (i, j, k) where i < j < k and prices[i] < prices[j] < prices[k],
5 * returns the maximum sum of profits[i] + profits[j] + profits[k].
6 *
7 * @param prices array of prices for each item
8 * @param profits array of profits for each item
9 * @return maximum profit sum for valid triplet, or -1 if no valid triplet exists
10 */
11 public int maxProfit(int[] prices, int[] profits) {
12 int n = prices.length;
13 int maxProfitSum = -1;
14
15 // Iterate through each potential middle element
16 for (int j = 0; j < n; ++j) {
17 // Find maximum profit from left side where price is less than prices[j]
18 int maxLeftProfit = 0;
19 for (int i = 0; i < j; ++i) {
20 if (prices[i] < prices[j]) {
21 maxLeftProfit = Math.max(maxLeftProfit, profits[i]);
22 }
23 }
24
25 // Find maximum profit from right side where price is greater than prices[j]
26 int maxRightProfit = 0;
27 for (int k = j + 1; k < n; ++k) {
28 if (prices[j] < prices[k]) {
29 maxRightProfit = Math.max(maxRightProfit, profits[k]);
30 }
31 }
32
33 // If valid triplet exists, update maximum profit sum
34 if (maxLeftProfit > 0 && maxRightProfit > 0) {
35 int currentProfitSum = maxLeftProfit + profits[j] + maxRightProfit;
36 maxProfitSum = Math.max(maxProfitSum, currentProfitSum);
37 }
38 }
39
40 return maxProfitSum;
41 }
42}
43
1class Solution {
2public:
3 int maxProfit(vector<int>& prices, vector<int>& profits) {
4 int n = prices.size();
5 int maxTotalProfit = -1;
6
7 // Iterate through each possible middle element j
8 for (int j = 0; j < n; ++j) {
9 int maxLeftProfit = 0; // Maximum profit from elements before j
10 int maxRightProfit = 0; // Maximum profit from elements after j
11
12 // Find the maximum profit from elements i where i < j and prices[i] < prices[j]
13 for (int i = 0; i < j; ++i) {
14 if (prices[i] < prices[j]) {
15 maxLeftProfit = max(maxLeftProfit, profits[i]);
16 }
17 }
18
19 // Find the maximum profit from elements k where k > j and prices[j] < prices[k]
20 for (int k = j + 1; k < n; ++k) {
21 if (prices[j] < prices[k]) {
22 maxRightProfit = max(maxRightProfit, profits[k]);
23 }
24 }
25
26 // If we found valid elements on both sides, update the maximum total profit
27 // This forms a triplet (i, j, k) where prices[i] < prices[j] < prices[k]
28 if (maxLeftProfit > 0 && maxRightProfit > 0) {
29 maxTotalProfit = max(maxTotalProfit, maxLeftProfit + profits[j] + maxRightProfit);
30 }
31 }
32
33 return maxTotalProfit;
34 }
35};
36
1/**
2 * Finds the maximum profit from selecting three items where their prices are in strictly increasing order
3 * @param prices - Array of prices for each item
4 * @param profits - Array of profits for each item
5 * @returns Maximum sum of profits for three items with strictly increasing prices, or -1 if not possible
6 */
7function maxProfit(prices: number[], profits: number[]): number {
8 const n: number = prices.length;
9 let maxProfitSum: number = -1;
10
11 // Iterate through each potential middle element
12 for (let middleIndex = 0; middleIndex < n; ++middleIndex) {
13 let maxLeftProfit: number = 0;
14 let maxRightProfit: number = 0;
15
16 // Find the maximum profit from items with price less than middle item's price (on the left)
17 for (let leftIndex = 0; leftIndex < middleIndex; ++leftIndex) {
18 if (prices[leftIndex] < prices[middleIndex]) {
19 maxLeftProfit = Math.max(maxLeftProfit, profits[leftIndex]);
20 }
21 }
22
23 // Find the maximum profit from items with price greater than middle item's price (on the right)
24 for (let rightIndex = middleIndex + 1; rightIndex < n; ++rightIndex) {
25 if (prices[middleIndex] < prices[rightIndex]) {
26 maxRightProfit = Math.max(maxRightProfit, profits[rightIndex]);
27 }
28 }
29
30 // If valid triplet exists, update the maximum profit sum
31 if (maxLeftProfit > 0 && maxRightProfit > 0) {
32 const currentTripletProfit: number = maxLeftProfit + profits[middleIndex] + maxRightProfit;
33 maxProfitSum = Math.max(maxProfitSum, currentTripletProfit);
34 }
35 }
36
37 return maxProfitSum;
38}
39
Time and Space Complexity
Time Complexity: O(nยฒ)
The algorithm uses a main loop that iterates through each element j
in the profits array (n iterations). For each position j
, it performs:
- A loop from index
0
toj-1
to find the maximum profit on the left side (up toj
iterations) - A loop from index
j+1
ton-1
to find the maximum profit on the right side (up ton-j-1
iterations)
For each j
, the total work is approximately j + (n-j-1) = n-1
operations. Since this happens for all n
positions, the total time complexity is O(n ร n) = O(nยฒ)
.
Space Complexity: O(1)
The algorithm only uses a constant amount of extra space:
- Variable
n
to store the array length - Variable
ans
to track the maximum profit - Variables
j
andx
for the main loop - Variables
left
andright
to store intermediate results - Loop variables
i
andk
All these variables use constant space regardless of the input size, resulting in O(1)
space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Initialization of Maximum Values
A common mistake is initializing max_left_profit
and max_right_profit
with -1
or negative infinity instead of 0
. This can lead to incorrect results when no valid left or right element exists.
Incorrect approach:
max_left_profit = -1 # Wrong! max_right_profit = -1 # Wrong!
Why it's wrong: If we initialize with -1
and no valid element exists on one side, we might still calculate a sum using -1
, leading to an incorrect total that's less than it should be. The check if max_left_profit and max_right_profit:
would also incorrectly pass since -1
is truthy.
Correct approach:
max_left_profit = 0 # Correct max_right_profit = 0 # Correct
2. Using Wrong Comparison for Updating Maximum Profits
Another pitfall is using <=
instead of <
when updating the maximum profit values, or forgetting to check if the new profit is actually better.
Incorrect approach:
# Wrong: accepts equal profits without checking if it's actually better if prices[i] < prices[j]: max_left_profit = profits[i] # Always updates, not checking maximum
Correct approach:
if prices[i] < prices[j] and profits[i] > max_left_profit: max_left_profit = profits[i]
3. Confusing Price and Profit Comparisons
A subtle but critical mistake is mixing up which array to use for comparisons. Prices determine validity, while profits determine optimality.
Incorrect approach:
# Wrong: comparing profits instead of prices for validity if profits[i] < profits[j] and profits[i] > max_left_profit: max_left_profit = profits[i]
Correct approach:
# Prices for validity check, profits for optimization if prices[i] < prices[j] and profits[i] > max_left_profit: max_left_profit = profits[i]
4. Edge Case: All Zeros in Profits Array
If the profits array contains zeros, the condition if max_left_profit and max_right_profit:
would fail even if valid non-zero profit items exist at position j
.
Better approach for handling zeros:
# Instead of relying on truthiness, explicitly check if valid elements were found
if max_left_profit > 0 and max_right_profit > 0:
max_profit_sum = max(max_profit_sum,
max_left_profit + current_profit + max_right_profit)
# Or track whether valid elements were found separately
left_found = False
right_found = False
for i in range(j):
if prices[i] < prices[j]:
left_found = True
max_left_profit = max(max_left_profit, profits[i])
However, this depends on whether the problem allows zero or negative profits. The original solution assumes all profits are positive based on the check logic.
Which type of traversal does breadth first search do?
Recommended Readings
Segment Tree Faster Range Queries For this article we want to introduce the idea of a Segment Tree Segment Trees allow us to quickly perform range queries as well as range updates Suppose we have an array and we want to know the sum of a particular range of numbers as well
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donโt Miss This!