You are given a list of events, where each event is represented as an array [startDay, endDay, value]
. You can attend at most k
events. For each event you attend, you must choose a day within its [startDay, endDay]
interval, and you cannot attend two events on the same day. Your goal is to maximize the total value you can obtain by attending up to k
events.
k
events in total.Constraints:
1 <= k <= events.length <= 10^4
1 <= startDay <= endDay <= 10^9
1 <= value <= 10^6
At first glance, the problem might look similar to the classic "Activity Selection" or "Weighted Interval Scheduling" problem, but with an added twist: you can attend up to k
events, and each event has a value. The brute-force approach would be to try every combination of up to k
events and select the one with the highest total value, but this quickly becomes infeasible due to the exponential number of combinations.
To optimize, we can recognize that:
k
events with maximum total value.We use a combination of dynamic programming (DP) and binary search to solve the problem efficiently:
startDay
. This allows us to process them in order and use binary search to find the next available event.dp[i][j]
represent the maximum value obtainable by considering the i
-th event and attending up to j
events.dp[i+1][j]
endDay
(found via binary search), decrementing j
by 1: value + dp[next][j-1]
startDay
is after the current event's endDay
.k
events or reached the end of the event list, return 0.This approach ensures we never consider overlapping events and always maximize the sum of values.
Sample Input:
events = [[1,2,4],[3,4,3],[2,3,1]], k = 2
[[1,2,4],[2,3,1],[3,4,3]]
n
be the number of events and k
the maximum events you can attend.
This problem is a variation of the weighted interval scheduling problem, extended to allow attending up to k
events. By sorting the events, using binary search to efficiently find the next non-overlapping event, and applying dynamic programming to avoid redundant computations, we achieve an efficient solution. The key insight is modeling the problem with a recursive DP that tracks both the event index and the count of events attended, ensuring we never double-book a day and always maximize the total value.
from bisect import bisect_right
from functools import lru_cache
class Solution:
def maxValue(self, events, k):
# Sort by start day
events.sort()
n = len(events)
# Precompute next indices using binary search
starts = [e[0] for e in events]
@lru_cache(None)
def dp(i, attended):
if attended == k or i == n:
return 0
# Option 1: Skip current event
res = dp(i+1, attended)
# Option 2: Attend current event
# Find next event whose start is after current end
next_i = bisect_right(starts, events[i][1])
res = max(res, events[i][2] + dp(next_i, attended+1))
return res
return dp(0, 0)
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
int maxValue(vector<vector<int>>& events, int k) {
sort(events.begin(), events.end());
int n = events.size();
vector<int> starts(n);
for (int i = 0; i < n; ++i) starts[i] = events[i][0];
// dp[i][attended]: max value starting at i, attended events so far
vector<vector<int>> dp(n+1, vector<int>(k+1, -1));
function<int(int, int)> dfs = [&](int i, int attended) {
if (attended == k || i == n) return 0;
if (dp[i][attended] != -1) return dp[i][attended];
// Option 1: skip
int res = dfs(i+1, attended);
// Option 2: take
int next_i = upper_bound(starts.begin(), starts.end(), events[i][1]) - starts.begin();
res = max(res, events[i][2] + dfs(next_i, attended+1));
return dp[i][attended] = res;
};
return dfs(0, 0);
}
};
import java.util.*;
class Solution {
public int maxValue(int[][] events, int k) {
Arrays.sort(events, (a, b) -> Integer.compare(a[0], b[0]));
int n = events.length;
int[] starts = new int[n];
for (int i = 0; i < n; ++i) starts[i] = events[i][0];
Integer[][] memo = new Integer[n][k+1];
return dfs(events, starts, 0, 0, k, memo);
}
private int dfs(int[][] events, int[] starts, int i, int attended, int k, Integer[][] memo) {
if (attended == k || i == events.length) return 0;
if (memo[i][attended] != null) return memo[i][attended];
// Option 1: skip
int res = dfs(events, starts, i+1, attended, k, memo);
// Option 2: take
int next = upperBound(starts, events[i][1]);
res = Math.max(res, events[i][2] + dfs(events, starts, next, attended+1, k, memo));
memo[i][attended] = res;
return res;
}
private int upperBound(int[] arr, int target) {
int low = 0, high = arr.length;
while (low < high) {
int mid = (low + high) / 2;
if (arr[mid] <= target) low = mid + 1;
else high = mid;
}
return low;
}
}
var maxValue = function(events, k) {
events.sort((a, b) => a[0] - b[0]);
let n = events.length;
let starts = events.map(e => e[0]);
let memo = Array.from({length: n}, () => Array(k+1).fill(undefined));
function upperBound(arr, target) {
let low = 0, high = arr.length;
while (low < high) {
let mid = Math.floor((low + high) / 2);
if (arr[mid] <= target) low = mid + 1;
else high = mid;
}
return low;
}
function dfs(i, attended) {
if (attended === k || i === n) return 0;
if (memo[i][attended] !== undefined) return memo[i][attended];
// Option 1: skip
let res = dfs(i+1, attended);
// Option 2: take
let next = upperBound(starts, events[i][1]);
res = Math.max(res, events[i][2] + dfs(next, attended+1));
memo[i][attended] = res;
return res;
}
return dfs(0, 0);
};