【P2280 [HNOI2003]激光炸弹】二维前缀和完整解题

一、题意分析

1. 题目条件

  1. 平面上存在 $n$ 个目标,每个目标坐标 $(x,y)$,拥有价值 $v$,同一坐标可叠加多个目标价值;
  2. 激光炸弹爆炸范围是边长为 $m$、边平行坐标轴的正方形;
  3. 规则:落在正方形四条边上的目标不会被摧毁,只有正方形内部的目标才计入总价值;
  4. 目标:求投放一颗炸弹,能摧毁的目标总价值最大值。

2. 样例解析

输入:

2 1
0 0 1
1 1 1

正方形边长 $m=1$,只有严格处于正方形内部的点才会被炸毁。

我们把所有坐标 $+1$ 偏移,原 $(0,0)$ 变为 $(1,1)$;当枚举右下角 $(1,1)$ 时,对应查询边长为1的矩形,区域内仅包含点 $(1,1)$,总价值为1,也就是样例输出。

点 $(1,1)$ 原坐标是 $(1,1)$,会落在边长为1的正方形边界上,无法选取。因此全局最大总价值为1。

3. 模型转化

边长为 $m$、不含边界的正方形,等价于:右下角为 $(i,j)$、左上角为 $(i-m,j-m)$ 的开区间矩形。

坐标整体加1偏移后,我们只需要查询左上角 $(i-m+1,j-m+1)$、右下角 $(i,j)$ 的闭矩形,刚好对应所有严格在正方形内部的点。

问题转化为经典静态二维矩形求和,使用二维前缀和求解。
请添加图片描述

二、核心算法:二维前缀和

1. 二维前缀和定义

设 $s[i][j]$ 代表从左上角 $(1,1)$ 到右下角 $(i,j)$ 整个矩形内所有目标的总价值。

递推公式:
$$
s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + val(i,j)
$$

  • $s[i-1][j]$:上方矩形总和;
  • $s[i][j-1]$:左侧矩形总和;
  • $s[i-1][j-1]$:左上角区域被重复计算,需要减去;
  • $val(i,j)$:当前坐标点叠加的目标价值。

2. 固定边长矩形求和公式

对于右下角 $(i,j)$、边长为 $m$ 的矩形,左上角坐标为 $(i-m,\ j-m)$,区域总价值:
$$
sum = s[i][j] - s[i-m][j] - s[i][j-m] + s[i-m][j-m]
$$
请添加图片描述

三、完整解题步骤

步骤1:初始化与读入数据

  1. 定义二维数组s存储坐标价值与前缀和,数组上限5005,覆盖坐标最大5000的范围;
  2. 读取目标总数 $n$、正方形边长 $m$;
  3. 循环读取每个目标的坐标 $x,y$、价值 $v$:
    • 执行 x++, y++ 坐标偏移,将原0开头坐标转为从1开始,规避前缀和下标0的边界判断;
    • s[x][y]上累加价值,处理同一坐标多个目标的叠加情况。

步骤2:预处理全局二维前缀和

双重循环遍历全部坐标范围(1~5001),套用二维前缀和递推公式,一次性预处理出所有矩形的总价值。

预处理完成后,任意矩形区域的总价值都可以在 $O(1)$ 时间内算出。

步骤3:枚举所有合法正方形,统计最大价值

  1. 枚举正方形右下角坐标 $i、j$,循环起点设置为 $m$,保证左上角坐标 $i-m、j-m$ 不会小于0,避免数组越界;
  2. 对每一组右下角,代入矩形求和公式,计算当前正方形能炸毁的目标总价值;
  3. 使用变量res持续记录遍历过程中出现的最大总价值。

步骤4:输出结果

遍历全部合法正方形后,res存储全局最优解,打印输出。

四、代码分段讲解

1. 常量与全局数组

const int N = 5005;
int n, m;  // n个目标 边长为m的正方形 
int s[N][N];  // 价值二维前缀和数组
  • $N=5005$:题目坐标最大值为5000,偏移后最大下标5001,数组预留安全空间;
  • s数组复用:先存储单点目标价值,预处理后存储前缀和。

2. 读入数据+坐标偏移

for(int i=0; i<n; i++){
    int x, y, v;
    scanf("%d%d%d",&x,&y,&v);
    x++,y++; // 坐标+1,消除下标0
    s[x][y]+=v;
}
  • x++,y++ 是关键优化,省去对 $i=0/j=0$ 的特殊边界判断;
  • s[x][y] += v:多个目标在同一坐标时,价值直接累加。

3. 二维前缀和预处理

for(int i=1; i<=5001; i++){
    for(int j = 1; j<=5001; j++){
        s[i][j] += s[i-1][j]+s[i][j-1]-s[i-1][j-1];
    }
}

严格套用二维前缀和递推公式,完成全图预处理。

4. 枚举所有正方形求最大值

int res = 0;
for(int i=m;i<=5001; i++){
    for(int j=m; j<=5001; j++){
        res = max(res, s[i][j]-s[i-m][j]-s[i][j-m]+s[i-m][j-m]);
    }
}
  1. 循环从m开始,保证左上角下标不越界;
  2. 代入固定边长矩形求和公式,计算当前正方形总价值;
  3. res = max(res, ...) 不断更新全局最优答案。

5. 输出

printf("%d\n",res);

打印一颗炸弹能炸毁的最大总价值。

五、完整代码实现

#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 5005;
int n, m;
int s[N][N];

int main() {
    scanf("%d%d", &n, &m);
    
    // 读入数据并坐标偏移
    for(int i = 0; i < n; i++) {
        int x, y, v;
        scanf("%d%d%d", &x, &y, &v);
        x++, y++; // 坐标+1,消除下标0
        s[x][y] += v;
    }
    
    // 预处理二维前缀和
    for(int i = 1; i <= 5001; i++) {
        for(int j = 1; j <= 5001; j++) {
            s[i][j] += s[i-1][j] + s[i][j-1] - s[i-1][j-1];
        }
    }
    
    // 枚举所有正方形求最大值
    int res = 0;
    for(int i = m; i <= 5001; i++) {
        for(int j = m; j <= 5001; j++) {
            int sum = s[i][j] - s[i-m][j] - s[i][j-m] + s[i-m][j-m];
            res = max(res, sum);
        }
    }
    
    printf("%d\n", res);
    return 0;
}

六、复杂度分析

  • 时间复杂度:$O(\max(5001^2, n))$

    • 读入数据:$O(n)$
    • 预处理前缀和:$O(5001^2)$
    • 枚举正方形:$O((5001-m)^2)$
    • 总体复杂度由预处理决定,约为 $2.5 \times 10^7$ 次操作,在1秒内可完成
  • 空间复杂度:$O(5005^2)$,约为 $25 \times 10^6$ 个int,约100MB,在题目限制内

七、题型总结

  1. 适用场景:静态二维网格、无修改操作、大量矩形求和查询;

  2. 核心要点

    • 坐标偏移:把0起点坐标转为1起点,简化前缀和边界逻辑;
    • 几何转化:不含边界的正方形等价固定边长矩形查询;
  3. 模板通用性:二维前缀和是信奥基础模板,可拓展到任意大小矩形求和问题;

  4. 拓展区分:如果题目存在单点、区间修改操作,则不能使用前缀和,需改用二维树状数组/二维线段树。

八、常见错误与调试技巧

1. 数组越界

  • 错误:循环边界处理不当,访问s[-1][j]s[i][-1]
  • 解决:确保所有下标从1开始,枚举时从m开始

2. 坐标偏移遗漏

  • 错误:忘记执行x++, y++,导致前缀和计算错误
  • 解决:在读入数据后立即偏移

3. 价值叠加处理

  • 错误:同一坐标多个目标时使用赋值而非累加
  • 解决:使用+=而非=

4. 边界条件

  • 当$m=0$时,需要特殊处理(但题目保证$m \geq 1$)
  • 当$m > 5000$时,炸弹覆盖整个区域,答案等于所有目标价值总和

九、测试用例

测试1:样例输入

2 1
0 0 1
1 1 1

输出:1

测试2:边界测试

1 5000
0 0 100

输出:100

测试3:多目标叠加

3 2
1 1 10
1 1 20
2 2 30

输出:60((1,1)和(2,2)都在边长为2的正方形内)

测试4:大范围测试

100 10
// 随机生成100个目标

需要验证算法效率

十、学习建议

  1. 掌握前置知识:一维前缀和 → 二维前缀和 → 三维前缀和
  2. 练习题目
  3. 拓展学习
    • 差分数组与前缀和的互逆关系
    • 二维树状数组(支持单点修改)
    • 二维线段树(支持区间修改查询)

总结:本题是二维前缀和的经典应用,关键在于理解坐标偏移的几何意义和前缀和的递推公式。掌握这个模板后,可以解决大量类似的静态矩形求和问题。

posted on 2026-07-10 17:48  5iCode  阅读(11)  评论(0)    收藏  举报

导航