Hard
题目描述
给你一个二维整数数组 intervals,其中 intervals[i] = [li, ri, weighti]。区间 i 从位置 li 开始,在 ri 结束,权重为 weighti。你可以选择最多 4 个非重叠区间。所选区间的得分定义为它们权重的总和。
返回具有最大得分的最多 4 个区间索引的字典序最小数组,表示你选择的非重叠区间。
如果两个区间不共享任何点,则称为非重叠。特别地,共享左边界或右边界的区间被认为是重叠的。
示例 1:
输入: intervals = [[1,3,2],[4,5,2],[1,5,5],[6,9,3],[6,7,1],[8,9,1]]
输出: [2,3]
解释: 你可以选择索引为 2 和 3 的区间,权重分别为 5 和 3。
示例 2:
输入: intervals = [[5,8,1],[6,7,7],[4,7,3],[9,10,6],[7,8,2],[11,14,3],[3,5,5]]
输出: [1,3,5,6]
解释: 你可以选择索引为 1、3、5 和 6 的区间,权重分别为 7、6、3 和 5。
约束条件:
1 <= intervals.length <= 5 * 10^4intervals[i].length == 3intervals[i] = [li, ri, weighti]1 <= li <= ri <= 10^91 <= weighti <= 10^9
解题思路
这是一个经典的区间调度优化问题,结合了动态规划和贪心策略。
核心思路:
- 排序策略:按照区间的右端点进行排序,这样可以保证在处理当前区间时,所有可能的前置区间都已经被考虑过了
- 状态设计:使用
dp[k][i]表示从前i个区间中选择最多k个非重叠区间的最大权重 - 状态转移:对于每个区间,有两种选择:
- 不选择当前区间:
dp[k][i] = dp[k][i-1] - 选择当前区间:需要找到最近的不重叠区间,使用二分查找优化
- 不选择当前区间:
算法步骤:
- 为每个区间添加原始索引,然后按右端点排序
- 使用动态规划计算最大权重,同时记录选择路径
- 通过回溯找到具体的区间选择方案
- 为了保证字典序最小,在权重相同时优先选择索引较小的方案
时间优化: 使用二分查找来快速定位最近的非重叠前置区间,将朴素的 O(n²) 优化为 O(n log n)。
代码实现
class Solution {
public:
vector<int> maximumWeight(vector<vector<int>>& intervals) {
int n = intervals.size();
vector<tuple<int, int, int, int>> sortedIntervals;
for (int i = 0; i < n; i++) {
sortedIntervals.push_back({intervals[i][1], intervals[i][0], intervals[i][2], i});
}
sort(sortedIntervals.begin(), sortedIntervals.end());
vector<vector<long long>> dp(5, vector<long long>(n + 1, 0));
vector<vector<int>> parent(5, vector<int>(n + 1, -1));
for (int i = 1; i <= n; i++) {
auto [r, l, w, idx] = sortedIntervals[i - 1];
for (int k = 1; k <= 4; k++) {
dp[k][i] = dp[k][i - 1];
parent[k][i] = parent[k][i - 1];
int lastNonOverlap = -1;
int left = 0, right = i - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (get<0>(sortedIntervals[mid]) < l) {
lastNonOverlap = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
long long newScore = w + (lastNonOverlap >= 0 ? dp[k - 1][lastNonOverlap + 1] : 0);
if (newScore > dp[k][i]) {
dp[k][i] = newScore;
parent[k][i] = lastNonOverlap + 1;
}
}
}
long long maxScore = 0;
int bestK = 0, bestI = 0;
for (int k = 1; k <= 4; k++) {
for (int i = 1; i <= n; i++) {
if (dp[k][i] > maxScore) {
maxScore = dp[k][i];
bestK = k;
bestI = i;
}
}
}
vector<int> result;
int curK = bestK, curI = bestI;
while (curK > 0 && curI > 0) {
int prevI = parent[curK][curI];
if (prevI != parent[curK][curI - 1]) {
result.push_back(get<3>(sortedIntervals[curI - 1]));
curK--;
curI = prevI;
} else {
curI--;
}
}
sort(result.begin(), result.end());
return result;
}
};
class Solution:
def maximumWeight(self, intervals: List[List[int]]) -> List[int]:
n = len(intervals)
sorted_intervals = []
for i in range(n):
sorted_intervals.append((intervals[i][1], intervals[i][0], intervals[i][2], i))
sorted_intervals.sort()
dp = [[0] * (n + 1) for _ in range(5)]
parent = [[-1] * (n + 1) for _ in range(5)]
for i in range(1, n + 1):
r, l, w, idx = sorted_intervals[i - 1]
for k in range(1, 5):
dp[k][i] = dp[k][i - 1]
parent[k][i] = parent[k][i - 1]
last_non_overlap = -1
left, right = 0, i - 1
while left <= right:
mid = (left + right) // 2
if sorted_intervals[mid][0] < l:
last_non_overlap = mid
left = mid + 1
else:
right = mid - 1
new_score = w + (dp[k - 1][last_non_overlap + 1] if last_non_overlap >= 0 else 0)
if new_score > dp[k][i]:
dp[k][i] = new_score
parent[k][i] = last_non_overlap + 1
max_score = 0
best_k, best_i = 0, 0
for k in range(1, 5):
for i in range(1, n + 1):
if dp[k][i] > max_score:
max_score = dp[k][i]
best_k, best_i = k, i
result = []
cur_k, cur_i = best_k, best_i
while cur_k > 0 and cur_i > 0:
prev_i = parent[cur_k][cur_i]
if prev_i != parent[cur_k][cur_i - 1]:
result.append(sorted_intervals[cur_i - 1][3])
cur_k -= 1
cur_i = prev_i
else:
cur_i -= 1
result.sort()
return result
public class Solution {
public int[] MaximumWeight(IList<IList<int>> intervals) {
int n = intervals.Count;
var sortedIntervals = new List<(int r, int l, int w, int idx)>();
for (int i = 0; i < n; i++) {
sortedIntervals.Add((intervals[i][1], intervals[i][0], intervals[i][2], i));
}
sortedIntervals.Sort();
var dp = new long[5][];
var parent = new int[5][];
for (int i = 0; i < 5; i++) {
dp[i] = new long[n + 1];
parent[i] = new int[n + 1];
Array.Fill(parent[i], -1);
}
for (int i = 1; i <= n; i++) {
var (r, l, w, idx) = sortedIntervals[i - 1];
for (int k = 1; k <= 4; k++) {
dp[k][i] = dp[k][i - 1];
parent[k][i] = parent[k][i - 1];
int lastNonOverlap = -1;
int left = 0, right = i - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (sortedIntervals[mid].r < l) {
lastNonOverlap = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
long newScore = w + (lastNonOverlap >= 0 ? dp[k - 1][lastNonOverlap + 1] : 0);
if (newScore > dp[k][i]) {
dp[k][i] = newScore;
parent[k][i] = lastNonOverlap + 1;
}
}
}
long maxScore = 0;
int bestK = 0, bestI = 0;
for (int k = 1; k <= 4; k++) {
for (int i = 1; i <= n; i++) {
if (dp[k][i] > maxScore) {
maxScore = dp[k][i];
bestK = k;
bestI = i;
}
}
}
var result = new List<int>();
int curK = bestK, curI = bestI;
while (curK > 0 && curI > 0) {
int prevI = parent[curK][curI];
if (prevI != parent[curK][curI - 1]) {
result.Add(sortedIntervals[curI - 1].idx);
curK--;
curI = prevI;
} else {
curI--;
}
}
result.Sort();
return result.ToArray();
}
}
var maximumWeight = function(intervals) {
const n = intervals.length;
const sortedIntervals = [];
for (let i = 0; i < n; i++) {
sortedIntervals.push([intervals[i][1], intervals[i][0], intervals[i][2], i]);
}
sortedIntervals.sort((a, b) => a[0] - b[0]);
const dp = Array(5).fill().map(() => Array(n + 1).fill(0));
const parent = Array(5).fill().map(() => Array(n + 1).fill(-1));
for (let i = 1; i <= n; i++) {
const [r, l, w, idx] = sortedIntervals[i - 1];
for (let k = 1; k <= 4; k++) {
dp[k][i] = dp[k][i - 1];
parent[k][i] = parent[k][i - 1];
let lastNonOverlap = -1;
let left = 0, right = i - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (sortedIntervals[mid][0] < l) {
lastNonOverlap = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
const newScore = w + (lastNonOverlap >= 0 ? dp[k - 1][lastNonOverlap + 1] : 0);
if (newScore > dp[k][i]) {
dp[k][i] = newScore;
parent[k][i] = lastNonOverlap + 1;
}
}
}
let maxScore = 0;
let bestK = 0, bestI = 0;
for (let k = 1; k <= 4; k++) {
for (let i = 1; i <= n; i++) {
if (dp[k][i] > maxScore) {
maxScore = dp[k][i];
bestK = k;
bestI = i;
}
}
}
const result = [];
let curK = bestK, curI = bestI;
while (curK > 0 && curI > 0) {
const prevI = parent[curK][curI];
if (prevI !== parent[curK][curI - 1]) {
result.push(sortedIntervals[curI - 1][3]);
curK--;
curI = prevI;
} else {
curI--;
}
}
result.sort((a, b) => a - b);
return result;
};
复杂度分析
| 复杂度类型 | 数值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n log n) | 排序需要 O(n log n),动态规划过程中每个状态需要二分查找 O(log n) |
| 空间复杂度 | O(n) | 需要 O(n) 的 dp 数组和 parent 数组存储状态信息 |
相关题目
- . Two Best Non-Overlapping Events (Medium)