Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
You have n robots. You are given two 0-indexed integer arrays, chargeTimes and runningCosts, both of length n. The ith robot costs chargeTimes[i] units to charge and costs runningCosts[i] units to run. You are also given an integer budget.
The total cost of running k chosen robots is equal to max(chargeTimes) + k * sum(runningCosts), where max(chargeTimes) is the largest charge cost among the k robots and sum(runningCosts) is the sum of running costs among the k robots.
Return the maximum number of consecutive robots you can run such that the total cost does not exceed budget.
Example 1:
Input: chargeTimes = [3,6,1,3,4], runningCosts = [2,1,3,4,5], budget = 25 Output: 3 Explanation: It is possible to run all individual and consecutive pairs of robots within budget. To obtain answer 3, consider the first 3 robots. The total cost will be max(3,6,1) + 3 * sum(2,1,3) = 6 + 3 * 6 = 24 which is less than 25. It can be shown that it is not possible to run more than 3 consecutive robots within budget, so we return 3.
Example 2:
Input: chargeTimes = [11,12,19], runningCosts = [10,8,7], budget = 19 Output: 0 Explanation: No robot can be run that does not exceed the budget, so we return 0.
Constraints:
chargeTimes.length == runningCosts.length == n1 <= n <= 5 * 1041 <= chargeTimes[i], runningCosts[i] <= 1051 <= budget <= 1015Problem summary: You have n robots. You are given two 0-indexed integer arrays, chargeTimes and runningCosts, both of length n. The ith robot costs chargeTimes[i] units to charge and costs runningCosts[i] units to run. You are also given an integer budget. The total cost of running k chosen robots is equal to max(chargeTimes) + k * sum(runningCosts), where max(chargeTimes) is the largest charge cost among the k robots and sum(runningCosts) is the sum of running costs among the k robots. Return the maximum number of consecutive robots you can run such that the total cost does not exceed budget.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Binary Search · Sliding Window · Monotonic Queue
[3,6,1,3,4] [2,1,3,4,5] 25
[11,12,19] [10,8,7] 19
sliding-window-maximum)kth-smallest-product-of-two-sorted-arrays)maximum-number-of-tasks-you-can-assign)minimized-maximum-of-products-distributed-to-any-store)minimum-time-to-complete-trips)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #2398: Maximum Number of Robots Within Budget
class Solution {
public int maximumRobots(int[] chargeTimes, int[] runningCosts, long budget) {
Deque<Integer> q = new ArrayDeque<>();
int n = chargeTimes.length;
int ans = 0;
long s = 0;
for (int l = 0, r = 0; r < n; ++r) {
s += runningCosts[r];
while (!q.isEmpty() && chargeTimes[q.peekLast()] <= chargeTimes[r]) {
q.pollLast();
}
q.offerLast(r);
while (!q.isEmpty() && (r - l + 1) * s + chargeTimes[q.peekFirst()] > budget) {
if (q.peekFirst() == l) {
q.pollFirst();
}
s -= runningCosts[l++];
}
ans = Math.max(ans, r - l + 1);
}
return ans;
}
}
// Accepted solution for LeetCode #2398: Maximum Number of Robots Within Budget
func maximumRobots(chargeTimes []int, runningCosts []int, budget int64) (ans int) {
q := Deque{}
s := int64(0)
l := 0
for r, t := range chargeTimes {
s += int64(runningCosts[r])
for !q.Empty() && chargeTimes[q.Back()] <= t {
q.PopBack()
}
q.PushBack(r)
for !q.Empty() && int64(r-l+1)*s+int64(chargeTimes[q.Front()]) > budget {
if q.Front() == l {
q.PopFront()
}
s -= int64(runningCosts[l])
l++
}
ans = max(ans, r-l+1)
}
return
}
// template
type Deque struct{ l, r []int }
func (q Deque) Empty() bool {
return len(q.l) == 0 && len(q.r) == 0
}
func (q Deque) Size() int {
return len(q.l) + len(q.r)
}
func (q *Deque) PushFront(v int) {
q.l = append(q.l, v)
}
func (q *Deque) PushBack(v int) {
q.r = append(q.r, v)
}
func (q *Deque) PopFront() (v int) {
if len(q.l) > 0 {
q.l, v = q.l[:len(q.l)-1], q.l[len(q.l)-1]
} else {
v, q.r = q.r[0], q.r[1:]
}
return
}
func (q *Deque) PopBack() (v int) {
if len(q.r) > 0 {
q.r, v = q.r[:len(q.r)-1], q.r[len(q.r)-1]
} else {
v, q.l = q.l[0], q.l[1:]
}
return
}
func (q Deque) Front() int {
if len(q.l) > 0 {
return q.l[len(q.l)-1]
}
return q.r[0]
}
func (q Deque) Back() int {
if len(q.r) > 0 {
return q.r[len(q.r)-1]
}
return q.l[0]
}
func (q Deque) Get(i int) int {
if i < len(q.l) {
return q.l[len(q.l)-1-i]
}
return q.r[i-len(q.l)]
}
# Accepted solution for LeetCode #2398: Maximum Number of Robots Within Budget
class Solution:
def maximumRobots(
self, chargeTimes: List[int], runningCosts: List[int], budget: int
) -> int:
q = deque()
ans = s = l = 0
for r, (t, c) in enumerate(zip(chargeTimes, runningCosts)):
s += c
while q and chargeTimes[q[-1]] <= t:
q.pop()
q.append(r)
while q and (r - l + 1) * s + chargeTimes[q[0]] > budget:
if q[0] == l:
q.popleft()
s -= runningCosts[l]
l += 1
ans = max(ans, r - l + 1)
return ans
// Accepted solution for LeetCode #2398: Maximum Number of Robots Within Budget
/**
* [2398] Maximum Number of Robots Within Budget
*/
pub struct Solution {}
// submission codes start here
impl Solution {
pub fn maximum_robots(charge_times: Vec<i32>, running_costs: Vec<i32>, budget: i64) -> i32 {
use std::collections::VecDeque;
let n = charge_times.len();
let mut result = 0;
let mut running_costs_sum = 0i64;
let mut queue = VecDeque::with_capacity(n);
// 最左侧的机器人
let mut left = 0;
for i in 0..n {
running_costs_sum += running_costs[i] as i64;
// 维持单调队列
while let Some(&pos) = queue.back() {
if charge_times[pos] <= charge_times[i] {
queue.pop_back();
} else {
break;
}
}
queue.push_back(i);
// 判断当前left开始的机器人是否会导致超过预算
while left <= i
&& (i - left + 1) as i64 * running_costs_sum
+ charge_times[*queue.front().unwrap()] as i64
> budget
{
if let Some(&front) = queue.front() {
if front == left {
queue.pop_front();
}
}
running_costs_sum -= running_costs[left] as i64;
left += 1;
}
if i >= left {
result = result.max(i - left + 1);
}
}
result as i32
}
}
// submission codes end
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_2398() {
assert_eq!(
3,
Solution::maximum_robots(vec![3, 6, 1, 3, 4], vec![2, 1, 3, 4, 5], 25)
);
assert_eq!(
0,
Solution::maximum_robots(vec![11, 12, 19], vec![10, 8, 7], 19)
);
}
}
// Accepted solution for LeetCode #2398: Maximum Number of Robots Within Budget
function maximumRobots(chargeTimes: number[], runningCosts: number[], budget: number): number {
const q = new Deque<number>();
const n = chargeTimes.length;
let [ans, s] = [0, 0];
for (let l = 0, r = 0; r < n; ++r) {
s += runningCosts[r];
while (!q.isEmpty() && chargeTimes[q.backValue()!] <= chargeTimes[r]) {
q.popBack();
}
q.pushBack(r);
while (!q.isEmpty() && (r - l + 1) * s + chargeTimes[q.frontValue()!] > budget) {
if (q.frontValue() === l) {
q.popFront();
}
s -= runningCosts[l++];
}
ans = Math.max(ans, r - l + 1);
}
return ans;
}
class Node<T> {
value: T;
next: Node<T> | null;
prev: Node<T> | null;
constructor(value: T) {
this.value = value;
this.next = null;
this.prev = null;
}
}
class Deque<T> {
private front: Node<T> | null;
private back: Node<T> | null;
private size: number;
constructor() {
this.front = null;
this.back = null;
this.size = 0;
}
pushFront(val: T): void {
const newNode = new Node(val);
if (this.isEmpty()) {
this.front = newNode;
this.back = newNode;
} else {
newNode.next = this.front;
this.front!.prev = newNode;
this.front = newNode;
}
this.size++;
}
pushBack(val: T): void {
const newNode = new Node(val);
if (this.isEmpty()) {
this.front = newNode;
this.back = newNode;
} else {
newNode.prev = this.back;
this.back!.next = newNode;
this.back = newNode;
}
this.size++;
}
popFront(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
const value = this.front!.value;
this.front = this.front!.next;
if (this.front !== null) {
this.front.prev = null;
} else {
this.back = null;
}
this.size--;
return value;
}
popBack(): T | undefined {
if (this.isEmpty()) {
return undefined;
}
const value = this.back!.value;
this.back = this.back!.prev;
if (this.back !== null) {
this.back.next = null;
} else {
this.front = null;
}
this.size--;
return value;
}
frontValue(): T | undefined {
return this.front?.value;
}
backValue(): T | undefined {
return this.back?.value;
}
getSize(): number {
return this.size;
}
isEmpty(): boolean {
return this.size === 0;
}
}
Use this to step through a reusable interview workflow for this problem.
Check every element from left to right until we find the target or exhaust the array. Each comparison is O(1), and we may visit all n elements, giving O(n). No extra space needed.
Each comparison eliminates half the remaining search space. After k comparisons, the space is n/2ᵏ. We stop when the space is 1, so k = log₂ n. No extra memory needed — just two pointers (lo, hi).
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Setting `lo = mid` or `hi = mid` can stall and create an infinite loop.
Usually fails on: Two-element ranges never converge.
Fix: Use `lo = mid + 1` or `hi = mid - 1` where appropriate.
Wrong move: Using `if` instead of `while` leaves the window invalid for multiple iterations.
Usually fails on: Over-limit windows stay invalid and produce wrong lengths/counts.
Fix: Shrink in a `while` loop until the invariant is valid again.