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.
robots, distance, and walls:robots[i] is the position of the ith robot.distance[i] is the maximum distance the ith robot's bullet can travel.walls[j] is the position of the jth wall.Every robot has one bullet that can either fire to the left or the right at most distance[i] meters.
A bullet destroys every wall in its path that lies within its range. Robots are fixed obstacles: if a bullet hits another robot before reaching a wall, it immediately stops at that robot and cannot continue.
Return the maximum number of unique walls that can be destroyed by the robots.
Notes:
Example 1:
Input: robots = [4], distance = [3], walls = [1,10]
Output: 1
Explanation:
robots[0] = 4 fires left with distance[0] = 3, covering [1, 4] and destroys walls[0] = 1.Example 2:
Input: robots = [10,2], distance = [5,1], walls = [5,2,7]
Output: 3
Explanation:
robots[0] = 10 fires left with distance[0] = 5, covering [5, 10] and destroys walls[0] = 5 and walls[2] = 7.robots[1] = 2 fires left with distance[1] = 1, covering [1, 2] and destroys walls[1] = 2.Input: robots = [1,2], distance = [100,1], walls = [10]
Output: 0
Explanation:
In this example, only robots[0] can reach the wall, but its shot to the right is blocked by robots[1]; thus the answer is 0.
Constraints:
1 <= robots.length == distance.length <= 1051 <= walls.length <= 1051 <= robots[i], walls[j] <= 1091 <= distance[i] <= 105robots are uniquewalls are uniqueProblem summary: There is an endless straight line populated with some robots and walls. You are given integer arrays robots, distance, and walls: robots[i] is the position of the ith robot. distance[i] is the maximum distance the ith robot's bullet can travel. walls[j] is the position of the jth wall. Every robot has one bullet that can either fire to the left or the right at most distance[i] meters. A bullet destroys every wall in its path that lies within its range. Robots are fixed obstacles: if a bullet hits another robot before reaching a wall, it immediately stops at that robot and cannot continue. Return the maximum number of unique walls that can be destroyed by the robots. Notes: A wall and a robot may share the same position; the wall can be destroyed by the robot at that position. Robots are not destroyed by bullets.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Binary Search · Dynamic Programming
[4] [3] [1,10]
[10,2] [5,1] [5,2,7]
[1,2] [100,1] [10]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3661: Maximum Walls Destroyed by Robots
class Solution {
private Integer[][] f;
private int[][] arr;
private int[] walls;
private int n;
public int maxWalls(int[] robots, int[] distance, int[] walls) {
n = robots.length;
arr = new int[n][2];
for (int i = 0; i < n; i++) {
arr[i][0] = robots[i];
arr[i][1] = distance[i];
}
Arrays.sort(arr, Comparator.comparingInt(a -> a[0]));
Arrays.sort(walls);
this.walls = walls;
f = new Integer[n][2];
return dfs(n - 1, 1);
}
private int dfs(int i, int j) {
if (i < 0) {
return 0;
}
if (f[i][j] != null) {
return f[i][j];
}
int left = arr[i][0] - arr[i][1];
if (i > 0) {
left = Math.max(left, arr[i - 1][0] + 1);
}
int l = lowerBound(walls, left);
int r = lowerBound(walls, arr[i][0] + 1);
int ans = dfs(i - 1, 0) + (r - l);
int right = arr[i][0] + arr[i][1];
if (i + 1 < n) {
if (j == 0) {
right = Math.min(right, arr[i + 1][0] - arr[i + 1][1] - 1);
} else {
right = Math.min(right, arr[i + 1][0] - 1);
}
}
l = lowerBound(walls, arr[i][0]);
r = lowerBound(walls, right + 1);
ans = Math.max(ans, dfs(i - 1, 1) + (r - l));
return f[i][j] = ans;
}
private int lowerBound(int[] arr, int target) {
int idx = Arrays.binarySearch(arr, target);
if (idx < 0) {
return -idx - 1;
}
return idx;
}
}
// Accepted solution for LeetCode #3661: Maximum Walls Destroyed by Robots
func maxWalls(robots []int, distance []int, walls []int) int {
type pair struct {
x, d int
}
n := len(robots)
arr := make([]pair, n)
for i := 0; i < n; i++ {
arr[i] = pair{robots[i], distance[i]}
}
sort.Slice(arr, func(i, j int) bool {
return arr[i].x < arr[j].x
})
sort.Ints(walls)
f := make(map[[2]int]int)
var dfs func(int, int) int
dfs = func(i, j int) int {
if i < 0 {
return 0
}
key := [2]int{i, j}
if v, ok := f[key]; ok {
return v
}
left := arr[i].x - arr[i].d
if i > 0 {
left = max(left, arr[i-1].x+1)
}
l := sort.SearchInts(walls, left)
r := sort.SearchInts(walls, arr[i].x+1)
ans := dfs(i-1, 0) + (r - l)
right := arr[i].x + arr[i].d
if i+1 < n {
if j == 0 {
right = min(right, arr[i+1].x-arr[i+1].d-1)
} else {
right = min(right, arr[i+1].x-1)
}
}
l = sort.SearchInts(walls, arr[i].x)
r = sort.SearchInts(walls, right+1)
ans = max(ans, dfs(i-1, 1)+(r-l))
f[key] = ans
return ans
}
return dfs(n-1, 1)
}
# Accepted solution for LeetCode #3661: Maximum Walls Destroyed by Robots
class Solution:
def maxWalls(self, robots: List[int], distance: List[int], walls: List[int]) -> int:
n = len(robots)
arr = sorted(zip(robots, distance), key=lambda x: x[0])
walls.sort()
@cache
def dfs(i: int, j: int) -> int:
if i < 0:
return 0
left = arr[i][0] - arr[i][1]
if i > 0:
left = max(left, arr[i - 1][0] + 1)
l = bisect_left(walls, left)
r = bisect_left(walls, arr[i][0] + 1)
ans = dfs(i - 1, 0) + r - l
right = arr[i][0] + arr[i][1]
if i + 1 < n:
if j == 0:
right = min(right, arr[i + 1][0] - arr[i + 1][1] - 1)
else:
right = min(right, arr[i + 1][0] - 1)
l = bisect_left(walls, arr[i][0])
r = bisect_left(walls, right + 1)
ans = max(ans, dfs(i - 1, 1) + r - l)
return ans
return dfs(n - 1, 1)
// Accepted solution for LeetCode #3661: Maximum Walls Destroyed by Robots
// Rust example auto-generated from java reference.
// Replace the signature and local types with the exact LeetCode harness for this problem.
impl Solution {
pub fn rust_example() {
// Port the logic from the reference block below.
}
}
// Reference (java):
// // Accepted solution for LeetCode #3661: Maximum Walls Destroyed by Robots
// class Solution {
// private Integer[][] f;
// private int[][] arr;
// private int[] walls;
// private int n;
//
// public int maxWalls(int[] robots, int[] distance, int[] walls) {
// n = robots.length;
// arr = new int[n][2];
// for (int i = 0; i < n; i++) {
// arr[i][0] = robots[i];
// arr[i][1] = distance[i];
// }
// Arrays.sort(arr, Comparator.comparingInt(a -> a[0]));
// Arrays.sort(walls);
// this.walls = walls;
// f = new Integer[n][2];
// return dfs(n - 1, 1);
// }
//
// private int dfs(int i, int j) {
// if (i < 0) {
// return 0;
// }
// if (f[i][j] != null) {
// return f[i][j];
// }
//
// int left = arr[i][0] - arr[i][1];
// if (i > 0) {
// left = Math.max(left, arr[i - 1][0] + 1);
// }
// int l = lowerBound(walls, left);
// int r = lowerBound(walls, arr[i][0] + 1);
// int ans = dfs(i - 1, 0) + (r - l);
//
// int right = arr[i][0] + arr[i][1];
// if (i + 1 < n) {
// if (j == 0) {
// right = Math.min(right, arr[i + 1][0] - arr[i + 1][1] - 1);
// } else {
// right = Math.min(right, arr[i + 1][0] - 1);
// }
// }
// l = lowerBound(walls, arr[i][0]);
// r = lowerBound(walls, right + 1);
// ans = Math.max(ans, dfs(i - 1, 1) + (r - l));
// return f[i][j] = ans;
// }
//
// private int lowerBound(int[] arr, int target) {
// int idx = Arrays.binarySearch(arr, target);
// if (idx < 0) {
// return -idx - 1;
// }
// return idx;
// }
// }
// Accepted solution for LeetCode #3661: Maximum Walls Destroyed by Robots
function maxWalls(robots: number[], distance: number[], walls: number[]): number {
type Pair = [number, number];
const n = robots.length;
const arr: Pair[] = robots.map((r, i) => [r, distance[i]]);
_.sortBy(arr, p => p[0]).forEach((p, i) => (arr[i] = p));
walls.sort((a, b) => a - b);
const f: number[][] = Array.from({ length: n }, () => Array(2).fill(-1));
function dfs(i: number, j: number): number {
if (i < 0) {
return 0;
}
if (f[i][j] !== -1) {
return f[i][j];
}
let left = arr[i][0] - arr[i][1];
if (i > 0) left = Math.max(left, arr[i - 1][0] + 1);
let l = _.sortedIndex(walls, left);
let r = _.sortedIndex(walls, arr[i][0] + 1);
let ans = dfs(i - 1, 0) + (r - l);
let right = arr[i][0] + arr[i][1];
if (i + 1 < n) {
if (j === 0) {
right = Math.min(right, arr[i + 1][0] - arr[i + 1][1] - 1);
} else {
right = Math.min(right, arr[i + 1][0] - 1);
}
}
l = _.sortedIndex(walls, arr[i][0]);
r = _.sortedIndex(walls, right + 1);
ans = Math.max(ans, dfs(i - 1, 1) + (r - l));
f[i][j] = ans;
return ans;
}
return dfs(n - 1, 1);
}
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: An incomplete state merges distinct subproblems and caches incorrect answers.
Usually fails on: Correctness breaks on cases that differ only in hidden state.
Fix: Define state so each unique subproblem maps to one DP cell.