预算内的最多机器人数目

标签: 队列 数组 二分查找 前缀和 滑动窗口 堆(优先队列)

难度: Hard

你有 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
解释:
可以在 budget 以内运行所有单个机器人或者连续运行 2 个机器人。
选择前 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 * 104
  • 1 <= chargeTimes[i], runningCosts[i] <= 105
  • 1 <= budget <= 1015

Submission

运行时间: 166 ms

内存: 21.9 MB

class Solution:
    def maximumRobots(self, chargeTimes: List[int], runningCosts: List[int], budget: int) -> int:
        n = len(chargeTimes)
        left = 0
        dq, costs = deque(), 0
        ans = 0
        for right in range(n):
            while dq and chargeTimes[right] > dq[-1]:
                dq.pop()
            dq.append(chargeTimes[right])
            costs += runningCosts[right]
            if dq[0] + (right - left + 1) * costs <= budget:
                ans = right - left + 1
            else:
                if dq[0] == chargeTimes[left]:
                    dq.popleft()
                costs -= runningCosts[left]
                left += 1
        return ans



Explain

The solution uses a sliding window approach combined with a deque to maintain the maximum charge time within the current window. As we iterate through the robots using a right pointer, we add the current robot's charge time to the deque in a way that ensures the deque is always sorted in descending order. This allows the largest element (maximum charge time) to always be at the front of the deque. We also keep a running sum of the running costs. At each step, we check if the sum of the maximum charge time and the product of the number of robots and the sum of running costs fits within the budget. If it does, we update our answer. If it doesn't, we adjust the window by moving the left pointer to the right, ensuring the properties of the deque and updating the running costs sum accordingly.

时间复杂度: O(n)

空间复杂度: O(n)

class Solution:
    def maximumRobots(self, chargeTimes: List[int], runningCosts: List[int], budget: int) -> int:
        n = len(chargeTimes)
        left = 0
        dq, costs = deque(), 0
        ans = 0
        for right in range(n):
            # Maintain a decreasing deque of charge times
            while dq and chargeTimes[right] > dq[-1]:
                dq.pop()
            dq.append(chargeTimes[right])
            # Update the total running costs for the current window
            costs += runningCosts[right]
            # Check if the current window fits the budget
            if dq[0] + (right - left + 1) * costs <= budget:
                ans = max(ans, right - left + 1)
            else:
                # Adjust the window from the left if budget is exceeded
                if dq[0] == chargeTimes[left]:
                    dq.popleft()
                costs -= runningCosts[left]
                left += 1
        return ans

Explore

这种做法是正确的,因为deque中只存储了当前滑动窗口内的元素,并且是按照其值的降序来存储的。当左指针left向右移动时,只有当deque的首元素等于chargeTimes[left]时,表示当前窗口的最大充电时间是由即将被移出窗口的元素提供的,因此需要将其从deque中移除。潜在的风险是如果未正确维护deque,即在其他操作中使deque状态与当前窗口状态不同步,那么这种直接检查的方法可能会导致错误。但在题解中的实现方式是正确且安全的,因为每次操作都确保了deque与窗口的同步。

当前解决方案能够正确处理chargeTimes中存在重复的最大值的情况。在deque中添加新元素时,如果新元素大于或等于deque中的元素,那么较小的元素会被移除,直到找到一个更大的元素或者deque为空。这保证了即使有重复的最大值,每个元素也只会在它属于当前窗口的最大值时才会处于deque的首位。在移动左指针时,只有当deque的首元素等于即将移出窗口的chargeTimes[left]时才会被移除,这确保了即使存在重复的最大值,也只有正确的元素会被移除。

在题解中,`costs`变量被用来累加从left到right的所有机器人的运行成本,因此它确实正确地表示了当前滑动窗口内所有机器人的运行成本之和。每次右指针right向右移动时,当前机器人的运行成本被加到`costs`上,如果窗口不符合预算条件,左指针left会向右移动并从`costs`中减去相应的运行成本。这保证了`costs`始终反映了当前窗口内机器人的总运行成本。因此,使用`(right - left + 1) * costs`来计算总运行成本是不正确的,应该直接使用`costs`,因为`costs`已经是总和,不需要再乘以机器人数量。