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

算法实战:机器人摧毁墙壁最大数目的线段树与离散化方案

本题要求在坐标范围极大的情况下,计算机器人向左或向右射击能摧毁的最大墙壁数量。核心难点在于坐标离散化处理以及避免重复统计重叠区域的墙壁。通过动态规划结合线段树维护区间最大值,并利用前缀墙数差值优化状态转移,可将复杂度降低至可接受范围。此外,利用墙壁仅能被相邻机器人摧毁的性质,还能进一步简化为线性 DP 方案。

灵魂摆渡发布于 2026/4/8更新于 2026/7/2259 浏览
算法实战:机器人摧毁墙壁最大数目的线段树与离散化方案

问题描述

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

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

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

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

示例: 输入:robots = [4,10], distance = [3,5], walls = [1,2,7,10] 输出:3 解释:机器人 0 向左覆盖 [1, 4],摧毁墙 1;机器人 1 向左覆盖 [5, 10],摧毁墙 7 和 10。

核心难点分析

这道题的直观解法似乎很容易想到动态规划,但有两个主要坑点:

  1. 坐标范围过大:坐标可达 10^9,直接开数组会导致内存超限(MLE)。
  2. 重叠统计:两个机器人的射击范围可能重叠,需要避免重复计算被摧毁的墙壁。

思路一:线段树 + 离散化

由于坐标空间巨大,我们首先需要对关键坐标进行离散化。关键坐标包括所有机器人的位置和墙壁的位置。

状态定义

设 dp[i] 表示处理到第 i 个机器人时,能摧毁的最大墙壁数。为了处理左右射击的依赖关系,我们需要维护一个线段树来查询区间最大值。

转移方程推导

假设当前机器人在 x2,向左能打到 x1,向右能打到 x3。

  • 向左射击:如果选择向左,它可能会与前一个向右射击的机器人产生冲突。我们需要确保不重复统计中间区域的墙。
  • 向右射击:同理,需考虑与前一个机器人的边界。

利用前缀墙数 g(x) 表示位置 ≤ x 的墙的数量,我们可以将区间墙数转化为差值形式。例如,[x1, x2] 之间的墙数为 g(x2) - g(x1)。

状态转移的核心在于最大化 dp[x] - g(x) 的值,这可以通过线段树维护区间最大值来实现。

代码实现要点

  1. 离散化类:封装排序、去重及映射逻辑。
  2. 线段树:支持单点更新和区间查询最大值。
  3. 主逻辑:按机器人位置排序后遍历,利用二分查找确定每个机器人实际可覆盖的离散化区间。
template <class T = int>
class CDiscretize {
public:
    CDiscretize(vector<T> nums) {
        (nums.(), nums.());
        nums.((nums.(), nums.()), nums.());
        m_nums = nums;
         ( i = ; i < nums.(); i++) {
            m_mValueToIndex[nums[i]] = i;
        }
    }
     []( T value)  {
         it = m_mValueToIndex.(value);
         (m_mValueToIndex.() == it)  ;
         it->second;
    }
    {  m_mValueToIndex.(); }
    vector<T> m_nums;
:
    unordered_map<T, > m_mValueToIndex;
};

 < ,  >
  {
:
    = ;
    = ;
    = ;
};

 < ,  >
  :  CSingeUpdateLineTree<TSave, TRecord> {
:
    ( iEleSize, TSave tDefault)
        : (iEleSize), (iEleSize * , tDefault), (tDefault) {}
    
    {
        (, , m_iEleSize - , index, update);
    }
    
    {
         (leftIndex, rightIndex, m_tDefault);
    }
    
    {  m_save[]; }

:
     m_iEleSize;
    {
         (iSaveLeft == iSaveRight) {
            ->(m_save[iNodeNO], iSaveLeft, update);
            ;
        }
          mid = iSaveLeft + (iSaveRight - iSaveLeft) / ;
         (iUpdateNO <= mid)
            (iNodeNO * , iSaveLeft, mid, iUpdateNO, update);
        
            (iNodeNO *  + , mid + , iSaveRight, iUpdateNO, update);
        ->(m_save[iNodeNO], m_save[iNodeNO * ], m_save[iNodeNO *  + ], iSaveLeft, iSaveRight);
    }
    
    {
         ((iSaveLeft >= iQueryLeft) && (iSaveRight <= iQueryRight)) {
            ->(ans, m_save[iNodeNO]);
            ;
        }
          mid = iSaveLeft + (iSaveRight - iSaveLeft) / ;
         (mid >= iQueryLeft)
            (ans, iNodeNO * , iSaveLeft, mid, iQueryLeft, iQueryRight);
         (mid +  <= iQueryRight)
            (ans, iNodeNO *  + , mid + , iSaveRight, iQueryLeft, iQueryRight);
    }
    
    vector<TSave> m_save;
     TSave m_tDefault;
};

 < ,  >
  :  CVectorSingUpdateLineTree<TSave, TRecord> {
:
     CVectorSingUpdateLineTree<TSave, TRecord>::CVectorSingUpdateLineTree;
:
    {
        ans = (ans, cur);
    }
    {
        save = (save, updatee);
    }
    {
        par = (left, r);
    }
};

  {
:
    {
         N = robots.();
        
        vector<pair<, >> rd;
        (walls.(), walls.());
         tmp = walls;
        tmp.(INT_MIN / ); 
         ( i = ; i < N; i++) {
            rd.(robots[i], distance[i]);
            tmp.(robots[i]);
        }
        
        ;
         (& i : walls) {
            i = disc[i];
        }
        (rd.(), rd.());
        
        vector<tuple<, , >> m_xs;
         M = disc.m_nums.();
        
         ( i = ; i < N; i++) {
             & [pos, dis] = rd[i];
              iLeftRobot = i ? rd[i - ].first : ;
              iLeft = (iLeftRobot, pos - dis);
              iRightRobot = (i +  == N) ? (INT_MAX / ) : rd[i + ].first;
              iRight = (iRightRobot, pos + dis);
            
              x1 = (disc.m_nums.(), disc.m_nums.(), iLeft) - disc.m_nums.();
              x2 = disc[pos];
              x3 = (disc.m_nums.(), disc.m_nums.(), iRight) - disc.m_nums.() - ;
            m_xs.(x1, x2, x3);
        }
        
        ;
        ;
        
         ( & [x1, x2, x3] : m_xs) {
            
              g2 = (walls.(), walls.(), x2) - walls.();
              g3 = (walls.(), walls.(), x3) - walls.();
              cnt1 = (walls.(), walls.(), x2) - (walls.(), walls.(), x1);
              cnt2 = (walls.(), walls.(), x3) - (walls.(), walls.(), x2);
            
              left = maxTree.(, x1 - ) + cnt1;
              right = maxTree.(, x2 - ) + cnt2;
              left2 = maxTree(x1, x2 - ) + g2;
              right2 = maxTree(x2, x3 - ) + g3;
            
            maxTree.(x2, (left, left2));
            maxTree.(x3, (right, right2));
            maxTree(x2, (left, left2) - g2);
            maxTree(x3, (right, right2) - g3);
        }
         maxTree.();
    }
};
sort
begin
end
erase
unique
begin
end
end
for
int
0
size
int
operator
const
const
auto
find
if
end
return
-1
return
int size() const
return
size
protected
int
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
public
CVectorSingUpdateLineTree
int
m_iEleSize
m_save
4
m_tDefault
void Update(int index, TRecord update)
Update
1
0
1
TSave Query(int leftIndex, int rightIndex)
return
Query
TSave QueryAll()
return
1
protected
int
void Update(int iNodeNO, int iSaveLeft, int iSaveRight, int iUpdateNO, TRecord update)
if
this
OnUpdate
return
const
int
2
if
Update
2
else
Update
2
1
1
this
OnUpdateParent
2
2
1
void Query(TSave& ans, int iNodeNO, int iSaveLeft, int iSaveRight, int iQueryLeft, int iQueryRight)
if
this
OnQuery
return
const
int
2
if
Query
2
if
1
Query
2
1
1
const
template
class
TSave
class
TRecord
class
CSetMaxLineTree
public
public
using
protected
virtual void OnQuery(TSave& ans, const TSave& cur)
max
virtual void OnUpdate(TSave& save, int iSave, const TRecord& updatee)
max
virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, int iSaveLeft, int iSaveRight)
max
class
Solution
public
int maxWalls(vector<int>& robots, vector<int>& distance, vector<int>& walls)
int
size
// 预处理:收集所有关键坐标进行离散化
int
int
sort
begin
end
auto
emplace_back
2
// 编码增加哨兵
for
int
0
emplace_back
emplace_back
CDiscretize<int> disc(tmp)
for
auto
sort
begin
end
int
int
int
int
size
for
int
0
const
auto
const
int
1
1
const
int
max
const
int
1
2
1
const
int
min
const
int
lower_bound
begin
end
begin
const
int
const
int
upper_bound
begin
end
begin
1
emplace_back
CSetMaxLineTree<int, int> maxTree(M + 1, 0)
CSetMaxLineTree<int, int> maxTree2(M + 1, -1000000)
for
const
auto
// 计算区间内的墙数
const
int
upper_bound
begin
end
begin
const
int
upper_bound
begin
end
begin
const
int
upper_bound
begin
end
lower_bound
begin
end
const
int
upper_bound
begin
end
lower_bound
begin
end
const
int
Query
0
1
const
int
Query
0
1
const
int
2.
Query
1
const
int
2.
Query
1
Update
max
Update
max
2.
Update
max
2.
Update
max
return
QueryAll

进阶思考:基于邻接性质的 DP

除了上述复杂的线段树方案,我们还可以挖掘问题的几何性质。观察发现,如果机器人和墙挨着一起,此墙一定被摧毁。更重要的是,由于子弹遇到机器人会停止,墙只会被相邻的机器人摧毁。

这意味着我们可以简化状态:

  • dp0[i]:第 i 个机器人向左射击时的最大得分。
  • dp1[i]:第 i 个机器人向右射击时的最大得分。

当 i-1 向右且 i 向左时,两者可能摧毁同一道墙,需要扣除重复部分。这种线性 DP 的空间复杂度仅为 O(N),无需离散化和线段树,适合对空间敏感的场景。

总结

解决此类坐标范围大且涉及区间覆盖的问题,通常遵循以下路径:

  1. 坐标压缩:使用离散化将大范围映射到小索引。
  2. 数据结构优化:利用线段树或树状数组维护区间极值。
  3. 数学转化:通过前缀和差值减少重复计算。

在实际工程中,优先尝试简化模型(如邻接 DP),若无法满足性能要求,再引入复杂的数据结构。

目录

  1. 问题描述
  2. 核心难点分析
  3. 思路一:线段树 + 离散化
  4. 状态定义
  5. 转移方程推导
  6. 代码实现要点
  7. 进阶思考:基于邻接性质的 DP
  8. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 基于 Nexent 构建 AI 智能体实现工作文档智能管理
  • 浏览器缓存机制详解与前端代码更新缓存处理方案
  • AI+AR智能眼镜年终盘点:巨头入场驱动产业提速,规模化拐点加速来临!
  • 使用 MCP 封装火山即梦 API 搭建 AI 绘画服务
  • 多模态大模型综述:视觉理解、生成与 Agent 研究进展
  • DeepSeek-R1 大模型基于 MS-Swift 框架的部署、推理与微调实践
  • OpenTiny NEXT 前端智能化系列直播:AI 前端与 WebAgent 学习路径
  • WriteGPT 人工智能写作框架使用指南
  • LangChain 开发入门教程:构建 LLM 驱动应用指南
  • 本地运行 LLM 的 AI 助手 Jan 部署与使用指南
  • 前端权限管理实战:构建安全可控的访问体系
  • AI 双重突破:MWC IQ 时代与 DeepSeek V4 多模态革命
  • 大模型驱动地图原理与实践:前端直连模型与完整 MCP 架构对比
  • 知网 AIGC 检测率过高应对策略与实践
  • 2025 年主流 AI 写作工具横向对比与选择指南
  • LLM 应用为何需要文本加载器及 LangChain 使用方法
  • DeepSeek 深度使用指南:提示词技巧与本地知识库搭建
  • Java String 核心机制与常用 API 实战
  • OpenCode 开源 AI 编程助手使用指南
  • GitHub Copilot Pro 学生免费认证与 VS Code 集成指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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