{x}
blog image

Two Best Non-Overlapping Events

You are given a 0-indexed 2D integer array of events where events[i] = [startTimei, endTimei, valuei]. The ith event starts at startTimei and ends at endTimei, and if you attend this event, you will receive a value of valuei. You can choose at most two non-overlapping events to attend such that the sum of their values is maximized.

Return this maximum sum.

Note that the start time and end time is inclusive: that is, you cannot attend two events where one of them starts and the other ends at the same time. More specifically, if you attend an event with end time t, the next event must start at or after t + 1.

 

Example 1:

Input: events = [[1,3,2],[4,5,2],[2,4,3]]
Output: 4
Explanation: Choose the green events, 0 and 1 for a sum of 2 + 2 = 4.

Example 2:

Example 1 Diagram
Input: events = [[1,3,2],[4,5,2],[1,5,5]]
Output: 5
Explanation: Choose event 2 for a sum of 5.

Example 3:

Input: events = [[1,5,3],[1,5,1],[6,6,5]]
Output: 8
Explanation: Choose events 0 and 2 for a sum of 3 + 5 = 8.

 

Constraints:

  • 2 <= events.length <= 105
  • events[i].length == 3
  • 1 <= startTimei <= endTimei <= 109
  • 1 <= valuei <= 106

Solution Explanation:

This problem asks us to find the maximum sum of values from at most two non-overlapping events. The solution uses a combination of sorting and binary search for efficient computation.

Approach:

  1. Sort Events: First, we sort the events array by their start times. This allows us to efficiently find non-overlapping events later.

  2. Calculate Maximum Value from Later Events: We create a f array. f[i] stores the maximum sum of values that can be obtained by attending events starting from index i onwards. We compute this array iteratively, from right to left: f[i] = max(f[i+1], events[i][2]). This means the maximum value from index i onwards is either the maximum value from index i+1 onwards, or the value of event i itself.

  3. Iterate and Find Maximum Sum: We iterate through the sorted events array. For each event:

    • We calculate its value (v).
    • We use binary search to find the index (idx) of the first event that starts after the current event ends (events[i][1]). This efficiently identifies the range of events that don't overlap with the current event.
    • If such an event exists (idx < n), we add the maximum value from that index onwards (f[idx]) to v.
    • We update ans (the maximum sum found so far) to be the maximum of ans and v.
  4. Return Result: The ans variable holds the maximum sum of values from at most two non-overlapping events.

Time Complexity Analysis:

  • Sorting the events takes O(N log N) time, where N is the number of events.
  • Constructing the f array takes O(N) time.
  • Iterating through the events and performing binary search for each event takes O(N log N) time.

Therefore, the overall time complexity of this solution is O(N log N), dominated by the sorting and binary search steps.

Space Complexity Analysis:

The space complexity is O(N), primarily due to the f array used to store the maximum values from suffixes of the sorted events.

Code Explanation (Python):

The Python code directly implements the approach described above. Let's break down the key parts:

class Solution:
    def maxTwoEvents(self, events: List[List[int]]) -> int:
        events.sort()  # Sort events by start time
        n = len(events)
        f = [events[-1][2]] * n # Initialize f array with the last event's value
        for i in range(n - 2, -1, -1):  # Calculate f array from right to left
            f[i] = max(f[i + 1], events[i][2])
 
        ans = 0
        for _, e, v in events:  # Iterate through events
            idx = bisect_right(events, e, key=lambda x: x[0]) #Binary search for non-overlapping event
            if idx < n:
                v += f[idx]  # Add maximum value from non-overlapping events
            ans = max(ans, v)  # Update maximum sum
        return ans

The bisect_right function from the bisect module efficiently finds the insertion point for a value in a sorted list, effectively performing the binary search. The lambda function lambda x: x[0] specifies that the comparison should be based on the start time (the first element) of each event.

The Java, C++, and Go solutions follow a very similar structure and logic, with the primary differences lying in syntax and standard library functions used for sorting and binary search. They all achieve the same time and space complexity.