跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

离散化与线段树解决机器人摧毁墙壁问题

LeetCode 3661 题“可以被机器人摧毁的最大墙壁数目”。问题涉及在直线上分布的机器人和墙壁,机器人可向左或向右发射子弹摧毁射程内的墙壁,但子弹会被其他机器人阻挡。文章探讨了动态规划结合线段树的解决方案,通过离散化处理坐标范围以节省内存,并修正了重叠射击范围的统计逻辑,最终计算出机器人能摧毁墙壁的最大数量。

ServerBase发布于 2026/4/6更新于 2026/10/24125 浏览
离散化与线段树解决机器人摧毁墙壁问题

题目描述

一条无限长的直线上分布着一些机器人和墙壁。给定整数数组 robots、distance 和 walls:

  • robots[i] 是第 i 个机器人的位置。
  • distance[i] 是第 i 个机器人的子弹可以行进的最大距离。
  • walls[j] 是第 j 堵墙的位置。

每个机器人有一颗子弹,可以向左或向右发射,最远距离为 distance[i] 米。子弹会摧毁其射程内路径上的每一堵墙。机器人是固定的障碍物:如果子弹在到达墙壁前击中另一个机器人,它会立即在该机器人处停止,无法继续前进。

返回机器人可以摧毁墙壁的最大数量。注意:墙壁和机器人可能在同一位置;该位置的墙壁可以被该位置的机器人摧毁。机器人不会被子弹摧毁。

示例 1: 输入:robots = [4], distance = [3], walls = [1,10] 输出:1 解释:robots[0] = 4 向左发射,distance[0] = 3,覆盖范围 [1, 4],摧毁了 walls[0] = 1。

提示:

  • 1 <= robots.length == distance.length <= 10^5
  • 1 <= walls.length <= 10^5
  • 1 <= robots[i], walls[j] <= 10^9
  • 1 <= distance[i] <= 10^5
  • robots 中的所有值都是互不相同的

解题思路

错误解法分析

初始尝试使用动态规划结合线段树。dp[i] 表示只摧毁位置 ≤ i 的墙,最后能销毁多少堵墙。按机器人位置从小到大处理,枚举向左或向右射击状态。

错误原因:两个机器人的射击范围不能重叠,否则会重复统计。例如 robots = {4,10}, distance = {3,3}, walls = {6,7,8}。第二个机器人向左射击能到 {7,8,9,10},第一个机器人向右射击到 7,第二个机器人向左射击到 8,若简单叠加会导致重复计算。

正确解法

核心思想:某个向右的机器人和某个向左的机器人射程重叠后,向右的机器人射程不变,向左的机器人缩短射程使之不重叠。向右射击仍然是一种状态,故主要讨论向左。

令向左的机器人位于 x2,向左能射击到 x1。则射程非最大向左的最大值为 max(dp[x] + f(x)),其中 f(x) 是处于 x+1 ~ x2 的墙数。令 g(x) 是 ≤ x 的墙数。则公式可转化为 g(x2) + max(dp[x] - g(x))。我们用最大值线段树记录 dp[x] - g(x)。

同时需要考虑内存限制。坐标范围可达 10^9,直接开数组会超限。解决方法包括:

  1. 离散化:将关键坐标映射到小范围索引。
  2. 改用最大值树状数组或优化线段树实现。

空间优化

通过离散化处理坐标,将大范围坐标压缩到 N 级别,配合线段树进行区间查询和单点更新,从而满足内存限制。

代码实现

#include <vector>
#include <algorithm>
#include <unordered_map>
#include <tuple>

using namespace std;

template<class T = int>
class CDiscretize {
public:
    CDiscretize(vector<T> nums) {
        sort(nums.begin(), nums.end());
        nums.erase(unique(nums.begin(), nums.end()), nums.end());
        m_nums = nums;
        for (int i = 0; i < nums.size(); i++) {
            m_mValueToIndex[nums[i]] = i;
        }
    }
    int operator[](const T value) const {
        auto it = m_mValueToIndex.find(value);
        if (m_mValueToIndex.end() == it) return -1;
        return it->second;
    }
    int size() const { return m_mValueToIndex.size(); }
    vector<T> m_nums;
protected:
    unordered_map<T, int> m_mValueToIndex;
};

template<class TSave, class TRecord>
class CSingeUpdateLineTree {
protected:
    virtual void OnQuery(TSave& ans, const TSave& cur) = 0;
    virtual void OnUpdate(TSave& save, int iSave, const TRecord& update) = 0;
    virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, int iSaveLeft, int iSaveRight) = 0;
};

template<class TSave, class TRecord>
class CVectorSingUpdateLineTree : public CSingeUpdateLineTree<TSave, TRecord> {
public:
    CVectorSingUpdateLineTree(int iEleSize, TSave tDefault) : m_iEleSize(iEleSize), m_save(iEleSize * 4, tDefault), m_tDefault(tDefault) {}
    void Update(int index, TRecord update) { Update(1, 0, m_iEleSize - 1, index, update); }
    TSave Query(int leftIndex, int rightIndex, TSave tDefault) {
        TSave ans = tDefault;
        Query(ans, 1, 0, m_iEleSize - 1, leftIndex, rightIndex);
        return ans;
    }
    TSave Query(int leftIndex, int rightIndex) { return Query(leftIndex, rightIndex, m_tDefault); }
    TSave QueryAll() { return m_save[1]; }
protected:
    int m_iEleSize;
    void Query(TSave& ans, int iNodeNO, int iSaveLeft, int iSaveRight, int iQueryLeft, int iQueryRight) {
        if ((iSaveLeft >= iQueryLeft) && (iSaveRight <= iQueryRight)) {
            this->OnQuery(ans, m_save[iNodeNO]);
            return;
        }
        if (iSaveLeft == iSaveRight) return;
        const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2;
        if (mid >= iQueryLeft) Query(ans, iNodeNO * 2, iSaveLeft, mid, iQueryLeft, iQueryRight);
        if (mid + 1 <= iQueryRight) Query(ans, iNodeNO * 2 + 1, mid + 1, iSaveRight, iQueryLeft, iQueryRight);
    }
    void Update(int iNodeNO, int iSaveLeft, int iSaveRight, int iUpdateNO, TRecord update) {
        if (iSaveLeft == iSaveRight) {
            this->OnUpdate(m_save[iNodeNO], iSaveLeft, update);
            return;
        }
        const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2;
        if (iUpdateNO <= mid) Update(iNodeNO * 2, iSaveLeft, mid, iUpdateNO, update);
        else Update(iNodeNO * 2 + 1, mid + 1, iSaveRight, iUpdateNO, update);
        this->OnUpdateParent(m_save[iNodeNO], m_save[iNodeNO * 2], m_save[iNodeNO * 2 + 1], iSaveLeft, iSaveRight);
    }
    vector<TSave> m_save;
    const TSave m_tDefault;
};

template<class TSave, class TRecord>
class CSetMaxLineTree : public CVectorSingUpdateLineTree<TSave, TRecord> {
public:
    using CVectorSingUpdateLineTree<TSave, TRecord>::CVectorSingUpdateLineTree;
protected:
    virtual void OnQuery(TSave& ans, const TSave& cur) { ans = max(ans, cur); }
    virtual void OnUpdate(TSave& save, int iSave, const TRecord& updatee) { save = max(save, updatee); }
    virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, int iSaveLeft, int iSaveRight) { par = max(left, r); }
};

class Solution {
public:
    int maxWalls(vector<int>& robots, vector<int>& distance, vector<int>& walls) {
        int N = robots.size();
        Init(robots, distance, walls);
        CSetMaxLineTree<int, int> maxTree(M + 1, 0);
        CSetMaxLineTree<int, int> maxTree2(M + 1, -1000000);
        for (const auto& [x1, x2, x3] : m_xs) {
            int g2 = upper_bound(walls.begin(), walls.end(), x2) - walls.begin();
            int g3 = upper_bound(walls.begin(), walls.end(), x3) - walls.begin();
            int cnt1 = upper_bound(walls.begin(), walls.end(), x2) - lower_bound(walls.begin(), walls.end(), x1);
            int left = maxTree.Query(0, x1 - 1) + cnt1;
            int cnt2 = upper_bound(walls.begin(), walls.end(), x3) - lower_bound(walls.begin(), walls.end(), x2);
            int right = maxTree.Query(0, x2 - 1) + cnt2;
            int left2 = maxTree2.Query(x1, x2 - 1) + g2;
            int right2 = maxTree2.Query(x2, x3 - 1) + g3;
            maxTree.Update(x2, max(left, left2));
            maxTree.Update(x3, max(right, right2));
            maxTree2.Update(x2, max(left, left2) - g2);
            maxTree2.Update(x3, max(right, right2) - g3);
        }
        return maxTree.QueryAll();
    }

private:
    int N, M;
    vector<tuple<int, int, int>> m_xs;

    void Init(const vector<int>& robots, const vector<int>& distance, vector<int>& walls) {
        vector<pair<int, int>> rd;
        sort(walls.begin(), walls.end());
        auto tmp = walls;
        tmp.emplace_back(INT_MIN / 2);
        for (int i = 0; i < N; i++) {
            rd.emplace_back(robots[i], distance[i]);
            tmp.emplace_back(robots[i]);
        }
        CDiscretize<int> disc(tmp);
        for (auto& i : walls) {
            i = disc[i];
        }
        sort(rd.begin(), rd.end());
        for (int i = 0; i < N; i++) {
            const auto& [pos, dis] = rd[i];
            const int iLeftRobot = i ? rd[i - 1].first : 1;
            const int iLeft = max(iLeftRobot, pos - dis);
            const int iRightRobot = (i + 1 == N) ? (INT_MAX / 2) : rd[i + 1].first;
            const int iRight = min(iRightRobot, pos + dis);
            const int x1 = lower_bound(disc.m_nums.begin(), disc.m_nums.end(), iLeft) - disc.m_nums.begin();
            const int x2 = disc[pos];
            const int x3 = upper_bound(disc.m_nums.begin(), disc.m_nums.end(), iRight) - disc.m_nums.begin() - 1;
            m_xs.emplace_back(x1, x2, x3);
        }
        M = disc.m_nums.size();
    }
};

目录

  1. 题目描述
  2. 解题思路
  3. 错误解法分析
  4. 正确解法
  5. 空间优化
  6. 代码实现

更多推荐文章

查看全部
  • OkHttp3 网络请求中的代理选择与路由策略详解
  • 国家精品公开课:Python 网络爬虫与数据分析实战
  • 2024 AI 大模型面试常见问题与答案解析
  • 最长有效括号:Python 动态规划解法
  • Stable Diffusion 局部重绘教程:轻松去除照片多余人物
  • 小狼毫输入法配置指南:拼音、五笔、注音方案设置
  • AI 绘画风格测试指南:5 个实用技巧掌握艺术生成
  • AI提示词:零基础入门与核心概念
  • ESP32-S3 部署 MimicLaw 集成 DeepSeek 与飞书机器人
  • LIBERO 数据集:终身机器人学习与知识迁移基准
  • MySQL 主从集群原理及 Docker 实战部署
  • MCP Server 实现 Excel 表格一键生成可视化图表 HTML 报告
  • Ubuntu 下 llama.cpp 编译与性能调优实战
  • Ubuntu 后端服务彻底卸载与安全清理指南
  • 行星减速器原理、计算公式与 C++ 实战
  • 分布式系统:负载均衡实战与场景分析
  • Poe 平台:Quora 推出的 AI 模型整合工具
  • Trae AI 将设计稿自动生成前端代码图文指南
  • Python 基础:标识符、数据类型与基本语句详解
  • M2FP 模型在 AR 导航中的人体交互应用

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online