Facebook Pixel

2907. Maximum Profitable Triplets With Increasing Prices I ๐Ÿ”’

MediumBinary Indexed TreeSegment TreeArray
Leetcode Link

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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Find the best item to the left of j with a smaller price
  2. 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 with prices[i] < prices[j]
  • We scan all positions k > j and track the maximum profit among items with prices[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:

  1. Initialize the answer: Start with ans = -1 to handle the case where no valid triplet exists.

  2. Enumerate the middle element: Loop through each index j from 0 to n-1, treating profits[j] as the middle element of our triplet. We store profits[j] as x for convenience.

  3. 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 from 0 to j-1
    • For each i, check if prices[i] < prices[j]
    • If true and profits[i] > left, update left = profits[i]
    • After this loop, left contains the maximum profit among all valid left elements
  4. 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 from j+1 to n-1
    • For each k, check if prices[j] < prices[k]
    • If true and profits[k] > right, update right = profits[k]
    • After this loop, right contains the maximum profit among all valid right elements
  5. Update the answer: If both left and right are non-zero (meaning we found valid elements on both sides):

    • Calculate the total profit as left + x + right where x is profits[j]
    • Update ans = max(ans, left + x + right)
  6. 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 Evaluator

Example 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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 to j-1 to find the maximum profit on the left side (up to j iterations)
  • A loop from index j+1 to n-1 to find the maximum profit on the right side (up to n-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 and x for the main loop
  • Variables left and right to store intermediate results
  • Loop variables i and k

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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which type of traversal does breadth first search do?


Recommended Readings

Want a Structured Path to Master System Design Too? Donโ€™t Miss This!

Load More