Medium
题目描述
在 X 轴上有一些奖品。给你一个按非递减顺序排列的整数数组 prizePositions,其中 prizePositions[i] 是第 i 个奖品的位置。在同一位置上可能有不同的奖品。同时给你一个整数 k。
你可以选择两个具有整数端点的线段。每个线段的长度必须是 k。你将收集位置落在至少一个选定线段内(包括线段端点)的所有奖品。两个选定的线段可以相交。
- 例如,如果
k = 2,你可以选择线段[1, 3]和[2, 4],你将赢得满足1 <= prizePositions[i] <= 3或2 <= prizePositions[i] <= 4的任何奖品 i。
如果你最优地选择两个线段,返回你能赢得的最大奖品数量。
示例 1:
输入:prizePositions = [1,1,2,2,3,3,5], k = 2
输出:7
解释:在这个例子中,你可以通过选择两个线段 [1, 3] 和 [3, 5] 来赢得所有 7 个奖品。
示例 2:
输入:prizePositions = [1,2,3,4], k = 0
输出:2
解释:对于这个例子,线段的一个选择是 [3, 3] 和 [4, 4],你将能够获得 2 个奖品。
提示:
1 <= prizePositions.length <= 10^51 <= prizePositions[i] <= 10^90 <= k <= 10^9prizePositions按非递减顺序排列。
解题思路
这道题的核心思路是先解决单个线段的最优解,然后考虑如何组合两个线段。
方法一:前缀最大值 + 滑动窗口
- 首先,我们需要能够快速计算以任意位置为右端点的长度为 k 的线段能覆盖的最大奖品数
- 使用滑动窗口技术,对于每个右端点位置,找到最左边能包含在长度为 k 的线段中的位置
- 维护一个前缀最大值数组,记录从开始到当前位置的所有可能线段中能获得的最大奖品数
- 对于每个位置作为第二个线段的右端点,结合前缀最大值就能得到两个线段的最优组合
算法步骤:
- 使用双指针维护滑动窗口,确保窗口内的位置范围不超过 k
- 计算每个位置作为右端点时单个线段能覆盖的奖品数
- 维护前缀最大值数组,记录到当前位置为止单个线段能获得的最大奖品数
- 枚举第二个线段的位置,结合前缀最大值计算两个线段的最大收益
时间复杂度为 O(n),空间复杂度为 O(n),其中 n 是奖品数量。
代码实现
class Solution {
public:
int maximizeWin(vector<int>& prizePositions, int k) {
int n = prizePositions.size();
vector<int> prefix(n, 0);
int left = 0, maxPrizes = 0;
for (int right = 0; right < n; right++) {
// 调整左边界,确保窗口长度不超过k
while (prizePositions[right] - prizePositions[left] > k) {
left++;
}
// 当前窗口大小
int currentPrizes = right - left + 1;
maxPrizes = max(maxPrizes, currentPrizes);
// 更新前缀最大值
prefix[right] = maxPrizes;
}
// 计算两个线段的最大收益
int result = 0;
left = 0;
for (int right = 0; right < n; right++) {
// 调整左边界
while (prizePositions[right] - prizePositions[left] > k) {
left++;
}
int currentPrizes = right - left + 1;
int prevMax = (left > 0) ? prefix[left - 1] : 0;
result = max(result, currentPrizes + prevMax);
}
return result;
}
};
class Solution:
def maximizeWin(self, prizePositions: List[int], k: int) -> int:
n = len(prizePositions)
prefix = [0] * n
left = 0
max_prizes = 0
# 计算前缀最大值
for right in range(n):
# 调整左边界,确保窗口长度不超过k
while prizePositions[right] - prizePositions[left] > k:
left += 1
# 当前窗口大小
current_prizes = right - left + 1
max_prizes = max(max_prizes, current_prizes)
# 更新前缀最大值
prefix[right] = max_prizes
# 计算两个线段的最大收益
result = 0
left = 0
for right in range(n):
# 调整左边界
while prizePositions[right] - prizePositions[left] > k:
left += 1
current_prizes = right - left + 1
prev_max = prefix[left - 1] if left > 0 else 0
result = max(result, current_prizes + prev_max)
return result
public class Solution {
public int MaximizeWin(int[] prizePositions, int k) {
int n = prizePositions.Length;
int[] prefix = new int[n];
int left = 0, maxPrizes = 0;
// 计算前缀最大值
for (int right = 0; right < n; right++) {
// 调整左边界,确保窗口长度不超过k
while (prizePositions[right] - prizePositions[left] > k) {
left++;
}
// 当前窗口大小
int currentPrizes = right - left + 1;
maxPrizes = Math.Max(maxPrizes, currentPrizes);
// 更新前缀最大值
prefix[right] = maxPrizes;
}
// 计算两个线段的最大收益
int result = 0;
left = 0;
for (int right = 0; right < n; right++) {
// 调整左边界
while (prizePositions[right] - prizePositions[left] > k) {
left++;
}
int currentPrizes = right - left + 1;
int prevMax = (left > 0) ? prefix[left - 1] : 0;
result = Math.Max(result, currentPrizes + prevMax);
}
return result;
}
}
var maximizeWin = function(prizePositions, k) {
const n = prizePositions.length;
const prefix = new Array(n).fill(0);
let left = 0, maxPrizes = 0;
// 计算前缀最大值
for (let right = 0; right < n; right++) {
// 调整左边界,确保窗口长度不超过k
while (prizePositions[right] - prizePositions[left] > k) {
left++;
}
// 当前窗口大小
const currentPrizes = right - left + 1;
maxPrizes = Math.max(maxPrizes, currentPrizes);
// 更新前缀最大值
prefix[right] = maxPrizes;
}
// 计算两个线段的最大收益
let result = 0;
left = 0;
for (let right = 0; right < n; right++) {
// 调整左边界
while (prizePositions[right] - prizePositions[left] > k) {
left++;
}
const currentPrizes = right - left + 1;
const prevMax = (left > 0) ? prefix[left - 1] : 0;
result = Math.max(result, currentPrizes + prevMax);
}
return result;
};
复杂度分析
| 算法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 前缀最大值 + 滑动窗口 | O(n) | O(n) |
其中 n 是 prizePositions 数组的长度。虽然有嵌套循环,但内层 while 循环的总执行次数不超过 n,因此整体时间复杂度仍为 O(n)。