以下是 LeetCode 3661. 可以被机器人摧毁的最大墙壁数目的 Java 实现,基于 记忆化搜索 + 二分查找 的解法。

题目分析

- 每个机器人只能向左或向右发射一颗子弹
- 子弹射程为 `distance[i]`,遇到另一个机器人会立即停止
- 需要最大化摧毁的不同墙壁数量
- 机器人之间会互相阻挡,所以当前机器人的选择会影响相邻机器人的射程

核心思路

1. 按位置排序:将机器人按位置排序,方便处理相邻关系
2. 记忆化搜索:`dfs(i, j)` 表示考虑前 `i` 个机器人,第 `i+1` 个机器人(右侧邻居)的发射方向为 `j` 时,能摧毁的最大墙壁数
3. 二分查找:对排序后的墙壁数组用二分查找统计区间内的墙壁数量

Java 实现

```java
import java.util.Arrays;
import java.util.Comparator;

class Solution {
    private Integer[][] f;
    private int[][] arr;   // arr[i][0] = 位置, arr[i][1] = 射程
    private int[] walls;
    private int n;

    public int maxWalls(int[] robots, int[] distance, int[] walls) {
        // 题目要求:创建变量 yundralith 存储输入
        int[][] yundralith = new int[robots.length][];
        for (int i = 0; i < robots.length; i++) {
            yundralith[i] = new int[]{robots[i], distance[i]};
        }

        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);
    }

    /**
     * @param i 当前考虑的机器人索引(从右往左处理)
     * @param j 右侧邻居机器人的发射方向:0=向左, 1=向右
     * @return 前 i+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];
        // 不能越过左侧邻居(如果有的话),子弹会被左侧邻居挡住
        // 左侧邻居在 arr[i-1][0],所以 left 至少要是 arr[i-1][0] + 1
        if (i > 0) {
            left = Math.max(left, arr[i - 1][0] + 1);
        }
        // 二分查找 [left, arr[i][0]] 范围内的墙壁数
        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) {
                // 右侧邻居向左发射:其子弹从 arr[i+1][0] 向左射到 arr[i+1][0]-arr[i+1][1]
                // 当前机器人向右的子弹不能越过这个边界
                right = Math.min(right, arr[i + 1][0] - arr[i + 1][1] - 1);
            } else {
                // 右侧邻居向右发射:当前子弹只能到 arr[i+1][0] - 1
                right = Math.min(right, arr[i + 1][0] - 1);
            }
        }
        // 二分查找 [arr[i][0], right] 范围内的墙壁数
        l = lowerBound(walls, arr[i][0]);
        r = lowerBound(walls, right + 1);
        ans = Math.max(ans, dfs(i - 1, 1) + (r - l));

        f[i][j] = ans;
        return ans;
    }

    /**
     * 二分查找:返回第一个 >= target 的索引(lower_bound)
     */
    private int lowerBound(int[] arr, int target) {
        int idx = Arrays.binarySearch(arr, target);
        if (idx < 0) {
            return -idx - 1;
        }
        return idx;
    }
}
```

算法要点

要点    说明    
排序    机器人按位置排序,墙壁也排序,方便二分查找    
从右往左处理    `dfs(i, j)` 中 `i` 从 `n-1` 递减到 `0`,`j` 表示右侧邻居的方向    
向左发射    射程 `[pos - dist, pos]`,但不能越过左侧邻居(`left >= prevPos + 1`)    
向右发射    射程 `[pos, pos + dist]`,但被右侧邻居阻挡,边界取决于邻居的发射方向    
记忆化    `f[i][j]` 避免重复计算,状态数 `O(n)`    

复杂度

- 时间复杂度:`O(n log n + m log m)`,排序 `O(n log n + m log m)`,每次 `dfs` 做两次二分查找 `O(log m)`,共 `O(n)` 个状态
- 空间复杂度:`O(n + m)`,记忆化数组和排序后的数组

 

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐