LeetCode:2398. 预算内的最多机器人数目 双指针+单调队列,时间复杂度O(n)
2398. 预算内的最多机器人数目
today 2398. 预算内的最多机器人数目
题目描述
你有 n
个机器人,给你两个下标从0
开始的整数数组 chargeTimes
和 runningCosts
,两者长度都为 n
。第 i
个机器人充电时间为 chargeTimes[i]
单位时间,花费 runningCosts[i]
单位时间运行。再给你一个整数 budget
。
运行 k
个机器人 总开销 是 max(chargeTimes) + k * sum(runningCosts)
,其中 max(chargeTimes)
是这 k 个机器人中最大充电时间,sum(runningCosts)
是这k
个机器人的运行时间之和。
请你返回在 不超过 budget
的前提下,你 最多 可以 连续 运行的机器人数目为多少。
示例1:
输入:
chargeTimes = [3,6,1,3,4]
runningCosts = [2,1,3,4,5]
budget = 25
输出:3
选择前 3 个机器人,可以得到答案最大值 3 。总开销是 max(3,6,1) + 3 * sum(2,1,3) = 6 + 3 * 6 = 24 ,小于 25 。
可以看出无法在 budget 以内连续运行超过 3 个机器人,所以我们返回 3 。
示例2:
输入:
chargeTimes = [11,12,19]
runningCosts = [10,8,7]
budget = 19
输出:0
解释:即使运行任何一个单个机器人,还是会超出 budget,所以我们返回 0 .
注意:
chargeTimes.length == runningCosts.length == n
1 <= n <= 5*10^4
1 <= chargeTimes[i], runningCosts[i] <= 10^5
1 <= budget <= 10^15
题目解析
解题思路:双指针+单调队列。
我们首先可以使用双指针来维护一个窗口,窗口的左右边界分别为 left
和 right
。 表示在left
和right
之间的机器人,可以正常运行。
如果left
和right
之间数值的和大于budget
,我们需要缩小窗口,向右移动left
,直到窗口内的机器人数目小于等于budget
。
同时,我们使用一个单调队列来维护窗口内机器人的chargeTimes
最大值。
遍历过程中right-left+1
的最大值,即为最多可以连续运行的机器人数目。
单调队列实现方法:
- 维护一个单调递减的队列
max_charge
,初始为空。 - 对于每个新加入机器人对应的
chargeTime
即chargeTimes[right]
,如果chargeTime
大于队尾元素,则将队尾元素弹出直到队尾元素大于等于chargeTime
,将chargeTime
入队。这样保证了max_charge
队列是单调递减的。 max_charge
队头元素,即为当前窗口内的最大充电时间。
复杂度分析:
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( n ) O(n) O(n)
代码实现
Python实现:
from collections import deque
class Solution:
def maximumRobots(self, chargeTimes, runningCosts, budget):
n = len(chargeTimes)
left = 0
sum_running_cost = 0
max_charge = deque() # 单调队列,用于存储当前窗口内的最大充电时间
res = 0
for right in range(n):
# 更新窗口内的运行成本总和
sum_running_cost += runningCosts[right]
# 维护单调队列,保证队列中的元素是递减的,以便快速获得窗口内最大充电时间
while max_charge and chargeTimes[right] > max_charge[-1]:
max_charge.pop()
max_charge.append(chargeTimes[right])
# 计算当前窗口的总开销
while max_charge and max_charge[0] + (right - left + 1) * sum_running_cost > budget:
# 如果超出预算,移动 left 缩小窗口,并更新相关值
if chargeTimes[left] == max_charge[0]:
max_charge.popleft()
sum_running_cost -= runningCosts[left]
left += 1
# 更新最大运行机器人数
res = max(res, right - left + 1)
return res
Go实现:
func maximumRobots(chargeTimes []int, runningCosts []int, budget int64) int {
n:=len(chargeTimes)
left,right:=0,0
sum_cost,ans:=0,0
max_charge:=make([]int,0)
for right=0;right<n;right++{
//更新窗口内的运行成本总和
sum_cost+=runningCosts[right]
//维护单调队列,保证队列中的元素是递减的,以便快速获得窗口内最大充电时间
for len(max_charge)>0 && max_charge[len(max_charge)-1]<chargeTimes[right]{
max_charge=max_charge[:len(max_charge)-1]
}
max_charge=append(max_charge,chargeTimes[right])
for left<=right && max_charge[0]+(right-left+1)*sum_cost>int(budget){
//如果超出预算,移动 left 缩小窗口,并更新相关值
if chargeTimes[left]==max_charge[0]{
max_charge=max_charge[1:]
}
sum_cost-=runningCosts[left]
left++
}
ans=max(ans,right-left+1)
}
return ans
}
C++实现:
class Solution {
public:
int maximumRobots(vector<int>& chargeTimes, vector<int>& runningCosts, long long budget) {
int n = chargeTimes.size();
int left = 0;
long long sum_cost = 0;
int ans = 0;
deque<int> max_charge;
for (int right = 0; right < n; right++) {
// 更新窗口内的运行成本总和
sum_cost += runningCosts[right];
// 维护单调队列,保证队列中的元素是递减的,以便快速获得窗口内最大充电时间
while (!max_charge.empty() && max_charge.back() < chargeTimes[right]) {
max_charge.pop_back();
}
max_charge.push_back(chargeTimes[right]);
// 如果超出预算,移动 left 缩小窗口,并更新相关值
while (left <= right && max_charge.front() + (right - left + 1) * sum_cost > budget) {
if (chargeTimes[left] == max_charge.front()) {
max_charge.pop_front(); // 移除最大充电时间
}
sum_cost -= runningCosts[left];
left++;
}
// 更新最大可运行的机器人数量
ans = max(ans, right - left + 1);
}
return ans;
}
};