二维差分算法高效解靶场问题

问题分析

给定一个 N×MN \times MN×M 的 01 矩阵(表示每个点是否被子弹中心击中)和子弹尺寸参数 l,rl, rl,r(子弹实际大小为 (2l+1)×(2r+1)(2l+1) \times (2r+1)(2l+1)×(2r+1)),需要输出最终靶子的状态矩阵(1 表示被摧毁,0 表示完好)。

关键规则
  1. 子弹中心被击中(输入矩阵中为 1)会摧毁以其为中心的矩形区域
  2. 摧毁范围:以中心点 (i,j)(i,j)(i,j) 为基准:
    • 行范围:[max⁡(0,i−r),min⁡(N−1,i+r)][\max(0, i-r), \min(N-1, i+r)][max(0,i−r),min(N−1,i+r)]
    • 列范围:[max⁡(0,j−l),min⁡(M−1,j+l)][\max(0, j-l), \min(M-1, j+l)][max(0,j−l),min(M−1,j+l)]
  3. 多个子弹区域可能重叠

高效算法:二维差分数组

使用二维差分数组 可在 O(NM)O(NM)O(NM) 时间复杂度内高效标记所有被摧毁区域,优于暴力方法的 O(NM⋅(2l+1)(2r+1))O(NM \cdot (2l+1)(2r+1))O(NM⋅(2l+1)(2r+1))。

算法步骤
  1. 初始化差分数组

    • 创建大小为 (N+2)×(M+2)(N+2) \times (M+2)(N+2)×(M+2) 的二维数组 diff(多出的空间用于边界处理)
    • 初始化为 0
  2. 标记摧毁区域

    • 遍历输入矩阵的每个点 (i,j)(i,j)(i,j):

      python 复制代码
      if grid[i][j] == '1':
          row_start = max(0, i - r)
          row_end = min(N-1, i + r)
          col_start = max(0, j - l)
          col_end = min(M-1, j + l)
          
          # 差分标记
          diff[row_start][col_start] += 1
          diff[row_start][col_end+1] -= 1
          diff[row_end+1][col_start] -= 1
          diff[row_end+1][col_end+1] += 1
  3. 计算前缀和得到覆盖状态

    • 创建结果数组 res(大小 N×MN \times MN×M)

    • 计算二维前缀和:

      python 复制代码
      for i in range(N):
          for j in range(M):
              # 前缀和公式
              res[i][j] = diff[i][j]
              if i > 0:
                  res[i][j] += res[i-1][j]
              if j > 0:
                  res[i][j] += res[i][j-1]
              if i > 0 and j > 0:
                  res[i][j] -= res[i-1][j-1]
              
              # 转换为 0/1 状态
              res[i][j] = '1' if res[i][j] > 0 else '0'
  4. 输出结果

    • res 按行输出为字符串

示例演示

输入

复制代码
2 2 1 1  # N=2, M=2, l=1, r=1
00
01       # 中心点 (1,1) 被击中

计算过程

  1. 子弹中心 (1,1) 的摧毁范围:[0,1]×[0,1][0,1] \times [0,1][0,1]×[0,1](整个矩阵)

  2. 差分标记:

    复制代码
    diff[0][0] += 1    → (0,0): +1
    diff[0][2] -= 1    → (0,2): -1(边界外)
    diff[2][0] -= 1    → (2,0): -1(边界外)
    diff[2][2] += 1    → (2,2): +1(边界外)
  3. 前缀和计算:

    坐标 计算过程 状态
    (0,0) 1 + 0 + 0 - 0 1 '1'
    (0,1) 0 + 1 + 0 - 0 1 '1'
    (1,0) 0 + 1 + 0 - 0 1 '1'
    (1,1) 0 + 1 + 1 - 1 1 '1'

输出

复制代码
11
11

完整代码实现

python 复制代码
def main():
    import sys
    data = sys.stdin.read().splitlines()
    if not data: 
        return
    
    # 解析第一行
    N, M, l, r = map(int, data[0].split())
    grid = data[1:1+N]
    
    # 初始化差分数组 (N+2) x (M+2)
    diff = [[0] * (M+2) for _ in range(N+2)]
    
    # 处理每个子弹中心
    for i in range(N):
        for j in range(M):
            if grid[i][j] == '1':
                row_start = max(0, i - r)
                row_end = min(N-1, i + r)
                col_start = max(0, j - l)
                col_end = min(M-1, j + l)
                
                # 差分标记
                diff[row_start][col_start] += 1
                diff[row_start][col_end+1] -= 1
                diff[row_end+1][col_start] -= 1
                diff[row_end+1][col_end+1] += 1
    
    # 计算二维前缀和
    res = [['0'] * M for _ in range(N)]
    for i in range(N):
        for j in range(M):
            # 继承上方和左侧的值
            if i > 0:
                diff[i][j] += diff[i-1][j]
            if j > 0:
                diff[i][j] += diff[i][j-1]
            if i > 0 and j > 0:
                diff[i][j] -= diff[i-1][j-1]
                
            # 转换为摧毁状态
            res[i][j] = '1' if diff[i][j] > 0 else '0'
    
    # 输出结果
    for row in res:
        print(''.join(row))

if __name__ == "__main__":
    main()

算法复杂度

  • 时间复杂度 :O(NM)O(NM)O(NM)
    • 遍历输入矩阵:O(NM)O(NM)O(NM)
    • 差分标记操作:每个子弹中心 O(1)O(1)O(1)
    • 前缀和计算:O(NM)O(NM)O(NM)
  • 空间复杂度 :O(NM)O(NM)O(NM)
    • 差分数组:(N+2)×(M+2)(N+2) \times (M+2)(N+2)×(M+2)
    • 结果数组:N×MN \times MN×M

边界处理

  1. 矩阵边界
    • 使用 max(0, ...)min(N-1/M-1, ...) 确保坐标不越界
  2. 差分数组
    • 额外增加 2 行/列防止边界溢出
  3. 重叠区域
    • 差分累加自动处理多次覆盖

此算法能高效处理 103×10310^3 \times 10^3103×103 规模的数据,满足绝大多数应用场景需求。

相关推荐
core51224 分钟前
深度解析DeepSeek-R1中GRPO强化学习算法
人工智能·算法·机器学习·deepseek·grpo
JosieBook27 分钟前
【Spring Boot】Spring Boot调用 WebService 接口的两种方式:动态调用 vs 静态调用 亲测有效
java·spring boot·后端
mit6.82429 分钟前
计数if|
算法
a程序小傲29 分钟前
京东Java面试被问:Spring拦截器和过滤器区别
java·面试·京东云·java八股文
sjg2001041439 分钟前
Deepin 20.9 误装gcc-8-base_8.4.0-1ubuntu1~16.04.1_amd64 后卸载
linux·运维·服务器
a伊雪1 小时前
c++ 引用参数
c++·算法
jerryinwuhan1 小时前
1210_1 Linux
linux·运维·服务器
Data_agent2 小时前
1688获得1688店铺列表API,python请求示例
开发语言·python·算法
2401_871260022 小时前
Java学习笔记(二)面向对象
java·python·学习
是梦终空2 小时前
计算机毕业设计252—基于Java+Springboot+vue3+协同过滤推荐算法的农产品销售系统(源代码+数据库+2万字论文)
java·spring boot·vue·毕业设计·源代码·协同过滤算法·农产品销售系统