Back to Month
HARD 12 Sep 2026 View on LeetCode

3414. Maximum Score of Non-overlapping Intervals

</> Solution

class Solution {
    public int[] maximumWeight(List<List<Integer>> intervals) {
        int n = intervals.size();
        
        // Step 1: Sort by right endpoint, then by left, then by weight, then by original index
        int[][] arr = new int[n][4]; // [l, r, weight, originalIndex]
        for (int i = 0; i < n; i++) {
            arr[i][0] = intervals.get(i).get(0); // l
            arr[i][1] = intervals.get(i).get(1); // r
            arr[i][2] = intervals.get(i).get(2); // weight
            arr[i][3] = i;                        // original index
        }
        Arrays.sort(arr, (a, b) -> a[1] != b[1] ? a[1] - b[1] : 
                                   a[0] != b[0] ? a[0] - b[0] : 
                                   a[2] != b[2] ? a[2] - b[2] : a[3] - b[3]);
        
        // Step 2: DP
        // dp[k][i] = {maxScore, lexSmallestIndices[]} 
        // choosing k intervals from first i intervals
        // We choose up to 4 intervals
        
        // State: dp[i][k] = best {score, indices[]} using k intervals, last ending at arr[i]
        // For space, store score and index array
        
        long[][] dpScore = new long[n][5];    // dpScore[i][k] = max score with k intervals, i-th as last
        int[][][] dpIdx = new int[n][5][];    // dpIdx[i][k] = lex smallest indices
        
        // Initialize
        for (long[] row : dpScore) Arrays.fill(row, -1);
        
        // Helper: compare two index arrays lexicographically
        // Returns true if a < b
        
        for (int i = 0; i < n; i++) {
            // k=1: just pick interval i
            dpScore[i][1] = arr[i][2];
            dpIdx[i][1] = new int[]{arr[i][3]};
            
            // Binary search for last interval j where arr[j][1] < arr[i][0]
            // (strictly less, since sharing boundary = overlapping)
            int lo = 0, hi = i - 1, prev = -1;
            while (lo <= hi) {
                int mid = (lo + hi) / 2;
                if (arr[mid][1] < arr[i][0]) {
                    prev = mid;
                    lo = mid + 1;
                } else {
                    hi = mid - 1;
                }
            }
            
            for (int k = 2; k <= 4; k++) {
                // Option 1: don't pick i → handled when we pick best across all i
                // Option 2: pick i + best (k-1) from prev
                if (prev >= 0) {
                    // Find best dp[j][k-1] for j in 0..prev
                    // (We'll handle this with prefix best arrays)
                }
            }
        }
        
        // Better approach: prefix best dp
        // best[k] = {bestScore, bestIndices} for exactly k chosen intervals so far
        
        long[] bestScore = new long[5];
        int[][] bestIdx = new int[5][];
        Arrays.fill(bestScore, -1);
        
        // dp over sorted intervals
        // For each interval i, we can extend any best[k-1] where last interval doesn't overlap
        
        // Store per-position best
        long[][] posScore = new long[n + 1][5];
        int[][][] posIdx  = new int[n + 1][5][];
        for (long[] row : posScore) Arrays.fill(row, -1);
        
        for (int i = 0; i < n; i++) {
            // Binary search: rightmost j < i where arr[j][1] < arr[i][0]
            int lo = 0, hi = i - 1, prev = -1;
            while (lo <= hi) {
                int mid = (lo + hi) / 2;
                if (arr[mid][1] < arr[i][0]) { prev = mid; lo = mid + 1; }
                else hi = mid - 1;
            }
            int p = prev + 1; // prefix index (1-based): best among 0..prev
            
            for (int k = 1; k <= 4; k++) {
                // Don't pick i: carry forward
                posScore[i + 1][k] = posScore[i][k];
                posIdx[i + 1][k]   = posIdx[i][k];
                
                // Pick i: need best (k-1) from 0..prev
                long prevS = (k == 1) ? 0 : posScore[p][k - 1];
                int[] prevI = (k == 1) ? new int[0] : posIdx[p][k - 1];
                
                if (k == 1 || prevS >= 0) {
                    long newScore = prevS + arr[i][2];
                    int[] newIdx = append(prevI, arr[i][3]);
                    Arrays.sort(newIdx);
                    
                    if (posScore[i + 1][k] < newScore || 
                       (posScore[i + 1][k] == newScore && isLess(newIdx, posIdx[i + 1][k]))) {
                        posScore[i + 1][k] = newScore;
                        posIdx[i + 1][k]   = newIdx;
                    }
                }
            }
        }
        
        // Find best across all k=1..4
        long best = -1;
        int[] ans = null;
        for (int k = 1; k <= 4; k++) {
            long s = posScore[n][k];
            int[] idx = posIdx[n][k];
            if (s < 0) continue;
            if (s > best || (s == best && isLess(idx, ans))) {
                best = s;
                ans  = idx;
            }
        }
        
        return ans == null ? new int[0] : ans;
    }
    
    private int[] append(int[] arr, int val) {
        int[] res = new int[arr.length + 1];
        System.arraycopy(arr, 0, res, 0, arr.length);
        res[arr.length] = val;
        return res;
    }
    
    // Returns true if a is lexicographically smaller than b
    private boolean isLess(int[] a, int[] b) {
        if (b == null) return true;
        if (a == null) return false;
        for (int i = 0; i < Math.min(a.length, b.length); i++) {
            if (a[i] != b[i]) return a[i] < b[i];
        }
        return a.length < b.length;
    }
}

TIME COMPLEXITY

O(n log n)

SPACE COMPLEXITY

O(n)

TOPICS

Array Binary Search Dynamic Programming Sorting