1. 激光炸弹问题解析:从暴力到优化的思维跃迁
第一次看到激光炸弹这道题时,我脑海中浮现的是最直接的暴力解法——枚举所有可能的正方形区域,然后逐个计算区域内目标价值总和。但当我看到数据范围(n≤10^4,坐标范围≤5000)时,立刻意识到这种O(n^2)的解法在竞赛中绝对会超时。这促使我深入思考如何利用前缀和技巧将时间复杂度优化到可接受的范围。
二维前缀和算法之所以能成为解决此类问题的银弹,核心在于它通过O(1)的查询时间替代了暴力解法的O(n^2)计算。想象你面前有一张巨大的网格纸,上面散布着不同价值的金币。前缀和就像是为这张纸预先计算好了所有从左上角出发到任意位置的金币累计数,让你能快速算出任意矩形区域内的财富总值。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 坐标偏移:细节决定成败的关键预处理
在实际编码中,坐标处理是第一个需要攻克的难点。题目给定的坐标范围是0到5000,但直接使用这些原始坐标会导致边界条件处理异常繁琐。我的第一次提交就因为没有处理好x=0或y=0的情况而WA(Wrong Answer)。
cpp复制// 错误示范:直接使用原始坐标
a[x][y] += w; // 当x或y为0时,计算前缀和会出现负索引
正确的做法是将所有坐标统一+1,使有效范围变为1到5001。这个看似简单的技巧却大有深意:
- 避免数组越界:C++中访问a[-1][3]会导致未定义行为
- 统一计算逻辑:所有位置的前缀和计算可以用相同公式处理
- 边界条件简化:不需要单独处理第一行和第一列的特殊情况
cpp复制// 正确做法:坐标偏移
x++, y++;
a[x][y] += w; // 现在坐标范围是[1,5001]
3. 二维前缀和的构建艺术
构建二维前缀和数组的过程就像是在完成一个精密的拼图。我习惯将其分解为三个清晰的步骤,这比直接套用双重循环更易于理解和调试:
3.1 初始化首行首列
cpp复制// 处理第一行:每个位置等于左边所有位置的和
for(int i=2; i<=M; i++) a[1][i] += a[1][i-1];
// 处理第一列:每个位置等于上方所有位置的和
for(int i=2; i<=M; i++) a[i][1] += a[i-1][1];
3.2 填充内部区域
这里使用的递推公式
