Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Move from brute-force thinking to an efficient approach using array strategy.
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:
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 <= 105events[i].length == 31 <= startTimei <= endTimei <= 1091 <= valuei <= 106Problem summary: 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.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Binary Search · Dynamic Programming
[[1,3,2],[4,5,2],[2,4,3]]
[[1,3,2],[4,5,2],[1,5,5]]
[[1,5,3],[1,5,1],[6,6,5]]
maximum-profit-in-job-scheduling)maximum-number-of-events-that-can-be-attended-ii)maximize-win-from-two-segments)maximum-score-of-non-overlapping-intervals)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #2054: Two Best Non-Overlapping Events
class Solution {
public int maxTwoEvents(int[][] events) {
Arrays.sort(events, (a, b) -> a[0] - b[0]);
int n = events.length;
int[] f = new int[n + 1];
for (int i = n - 1; i >= 0; --i) {
f[i] = Math.max(f[i + 1], events[i][2]);
}
int ans = 0;
for (int[] e : events) {
int v = e[2];
int left = 0, right = n;
while (left < right) {
int mid = (left + right) >> 1;
if (events[mid][0] > e[1]) {
right = mid;
} else {
left = mid + 1;
}
}
if (left < n) {
v += f[left];
}
ans = Math.max(ans, v);
}
return ans;
}
}
// Accepted solution for LeetCode #2054: Two Best Non-Overlapping Events
func maxTwoEvents(events [][]int) int {
sort.Slice(events, func(i, j int) bool {
return events[i][0] < events[j][0]
})
n := len(events)
f := make([]int, n+1)
for i := n - 1; i >= 0; i-- {
f[i] = max(f[i+1], events[i][2])
}
ans := 0
for _, e := range events {
v := e[2]
left, right := 0, n
for left < right {
mid := (left + right) >> 1
if events[mid][0] > e[1] {
right = mid
} else {
left = mid + 1
}
}
if left < n {
v += f[left]
}
ans = max(ans, v)
}
return ans
}
# Accepted solution for LeetCode #2054: Two Best Non-Overlapping Events
class Solution:
def maxTwoEvents(self, events: List[List[int]]) -> int:
events.sort()
n = len(events)
f = [events[-1][2]] * n
for i in range(n - 2, -1, -1):
f[i] = max(f[i + 1], events[i][2])
ans = 0
for _, e, v in events:
idx = bisect_right(events, e, key=lambda x: x[0])
if idx < n:
v += f[idx]
ans = max(ans, v)
return ans
// Accepted solution for LeetCode #2054: Two Best Non-Overlapping Events
impl Solution {
pub fn max_two_events(mut events: Vec<Vec<i32>>) -> i32 {
events.sort_by(|a, b| a[0].cmp(&b[0]));
let n: usize = events.len();
let mut f: Vec<i32> = vec![0; n + 1];
for i in (0..n).rev() {
f[i] = f[i + 1].max(events[i][2]);
}
let mut ans: i32 = 0;
for e in &events {
let mut v: i32 = e[2];
let mut left: usize = 0;
let mut right: usize = n;
while left < right {
let mid = (left + right) >> 1;
if events[mid][0] > e[1] {
right = mid;
} else {
left = mid + 1;
}
}
if left < n {
v += f[left];
}
ans = ans.max(v);
}
ans
}
}
// Accepted solution for LeetCode #2054: Two Best Non-Overlapping Events
function maxTwoEvents(events: number[][]): number {
events.sort((a, b) => a[0] - b[0]);
const n = events.length;
const f: number[] = Array(n + 1).fill(0);
for (let i = n - 1; ~i; --i) {
f[i] = Math.max(f[i + 1], events[i][2]);
}
let ans = 0;
for (const [_, end, v] of events) {
let [left, right] = [0, n];
while (left < right) {
const mid = (left + right) >> 1;
if (events[mid][0] > end) {
right = mid;
} else {
left = mid + 1;
}
}
const t = left < n ? f[left] : 0;
ans = Math.max(ans, t + v);
}
return ans;
}
Use this to step through a reusable interview workflow for this problem.
Check every element from left to right until we find the target or exhaust the array. Each comparison is O(1), and we may visit all n elements, giving O(n). No extra space needed.
Each comparison eliminates half the remaining search space. After k comparisons, the space is n/2ᵏ. We stop when the space is 1, so k = log₂ n. No extra memory needed — just two pointers (lo, hi).
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Setting `lo = mid` or `hi = mid` can stall and create an infinite loop.
Usually fails on: Two-element ranges never converge.
Fix: Use `lo = mid + 1` or `hi = mid - 1` where appropriate.
Wrong move: An incomplete state merges distinct subproblems and caches incorrect answers.
Usually fails on: Correctness breaks on cases that differ only in hidden state.
Fix: Define state so each unique subproblem maps to one DP cell.