本文本质上为本人对力扣题解区灵茶山艾符的题解的理解.
这道题虽然是困难题,但没有难到离谱的地步,不要被困难吓到啦.
题目要求最多可以连续运行的机器人数目为多少,即求子数组问题,那我们依旧可以用滑动窗口来解决.而跟滑动窗口板子题相比,这道题对于开销的定义为max(chargeTimes) + k * sum(runningCosts)
sum(runningCosts)就可视作最基础的滑动窗口所维护的区间总值,那我们要解决的问题就变为了如何找到[left, right]区间中的max(chargeTimes).
//入队
while (!q.empty() && chargeTimes[right] >= chargeTimes[q.back()]) {
q.pop_back();
}
q.push_back(right);
sum += runningCosts[right];
//出队
while (!q.empty() && chargeTimes[q.front()] + (right - left + 1) * sum > budget) {
if (left == q.front()) {
q.pop_front();
}
}
运用deque(双端队列)可以很轻松的解决(如上述代码).在这段代码中,较难理解的就是入队部分.为什么比chargeTimes[right]小的队尾出队之后就不要入队了呢,这样难道不会影响right左边的值进而影响最大值吗.注意啦注意啦,我们要维护的只是max(chargeTimes),如果chargeTimes[right]是区间内的最大值的话,在现在right所在的位置变为区间的左端点之前,区间的最大值是不会小于chargeTimes[right]的,我们可以将这部分理解为一个单调栈.
再看出队,上述入队还可以用单调栈代替,我们没有必要用双端队列呀,别急,出队时就要用到这一点了.当出队影响max(chargeTimes)最大值的时候,就要从队头出列了.此时又诞生了一个疑问,我们的队列有没有可能是空的呢,这点大可不必担心,在right作为最大值清空队列成为队头后,我们将窗口右移,如果下一个比chargeTimes[right],我们依旧让他入队,换而言之,right只影响在right之前的值.
至此,我们已经解决了如何维护max(chargeTimes),问题迎刃而解.下面附上题解代码
class Solution {
public:
int maximumRobots(vector<int>& chargeTimes, vector<int>& runningCosts, long long budget) {
int res = 0, left = 0;
long long sum = 0;
deque<int> q;
for (int right = 0; right < chargeTimes.size(); right++) {
while (!q.empty() && chargeTimes[right] >= chargeTimes[q.back()]) {
q.pop_back();
}
q.push_back(right);
sum += runningCosts[right];
while (!q.empty() && chargeTimes[q.front()] + (right - left + 1) * sum > budget) {
if (left == q.front()) {
q.pop_front();
}
sum -= runningCosts[left];
left++;
}
res = max(res, right - left + 1);
}
return res;
}
};