3661. Maximum Walls Destroyed by Robots

  • Array
  • Greedy
  • Sorting
  • Binary Search
  • Dynamic Programming
TIME
O(n log n + m log m)
SPACE
O(n)
problem on leetcode ↗source on github ↗

Intuition

Each robot can fire left OR right. Bullets are blocked by adjacent robots. Use DP to track the best outcome when each robot fires left vs right, accounting for overlapping wall coverage between adjacent robots.

Solution

Step 1: Sort Robots by Position

Sort robots by position to determine blocking by adjacent robots.

Step 2: Compute Intervals for Each Robot

For each robot at position p with range d:

Left shot interval: [max(p - d, prev_robot + 1), p] Right shot interval: [p, min(p + d, next_robot - 1)]

Use binary search to find wall indices within each interval.

Step 3: Count Walls and Overlaps

  • left[i] = walls destroyed if robot i fires left
  • right[i] = walls destroyed if robot i fires right
  • overlap[i] = walls in intersection of right[i-1] and left[i] intervals

Overlap matters because if robot i-1 fires right and robot i fires left, they may both cover the same walls (counted twice).

Step 4: DP Transition

Track two states:

  • sub_left = max walls if current robot fires left
  • sub_right = max walls if current robot fires right

For each robot i:

current_left = max(sub_left + left[i], sub_right + left[i] - overlap[i])
current_right = max(sub_left + right[i], sub_right + right[i])

The overlap subtraction handles double-counting when adjacent robots' ranges intersect.

Complexity

  • Time: O(n log n + m log m), sorting and binary search
  • Space: O(n), interval arrays and DP states

Edge Cases

  • Single robot → max of left or right shot
  • No walls in range → return 0
  • All robots blocking each other → minimal coverage
  • Walls at robot positions → included in both left and right

Code

from bisect import bisect_left, bisect_right
from typing import List


class Solution:
    def maxWallsDestroyed(self, robots: List[int], distance: List[int], walls: List[int]) -> int:
        """
        3661. Maximum Walls Destroyed by Robots
        Time: O(n log n + m log m)
        Space: O(n)
        """
        n = len(robots)
        robots_with_d = sorted(zip(robots, distance))
        robots = [r for r, _ in robots_with_d]
        dist = {r: d for r, d in robots_with_d}
        walls.sort()

        left_l = [0] * n
        left_r = [0] * n
        right_l = [0] * n
        right_r = [0] * n

        for i in range(n):
            p = robots[i]
            d = dist[p]

            L = p - d
            if i > 0:
                L = max(L, robots[i - 1] + 1)

            left_l[i] = bisect_left(walls, L)
            left_r[i] = bisect_right(walls, p) - 1

            R = p + d
            if i < n - 1:
                R = min(R, robots[i + 1] - 1)

            right_l[i] = bisect_left(walls, p)
            right_r[i] = bisect_right(walls, R) - 1

        def count(l, r):
            return max(0, r - l + 1)

        left = [count(left_l[i], left_r[i]) for i in range(n)]
        right = [count(right_l[i], right_r[i]) for i in range(n)]

        overlap = [0] * n
        for i in range(1, n):
            l = max(right_l[i - 1], left_l[i])
            r = min(right_r[i - 1], left_r[i])
            overlap[i] = max(0, r - l + 1)

        sub_left = left[0]
        sub_right = right[0]

        for i in range(1, n):
            current_left = max(
                sub_left + left[i],
                sub_right + left[i] - overlap[i]
            )

            current_right = max(
                sub_left + right[i],
                sub_right + right[i]
            )

            sub_left, sub_right = current_left, current_right

        return max(sub_left, sub_right)