前置知识
前缀和和二维前缀和是常用的算法技巧,用于快速求解区间和问题,尤其在处理高频查询时表现出色。
前缀和(Prefix Sum)
前缀和是一种针对一维数组的技巧,主要用于高效求解数组中某个区间元素之和。核心思想是通过预处理,将每个位置的累积和保存起来,后续查询可以在常数时间内完成。
-
构造过程: 构造前缀和数组时,第i个位置存储的是数组从开始到第i个元素的累加和。即:
其中 表示前缀和数组, 表示原始数组。
-
求区间和: 对于区间 的和,只需用公式:
当 时,直接为 。
-
常用场景:
- 求多次区间和问题,例如统计成绩、数据分析。
- 判断连续子数组是否满足特定条件,如最大子数组和问题。
- 快速统计数组中满足条件的子数组个数。
二维前缀和(2D Prefix Sum)
二维前缀和是前缀和在二维数组中的扩展,用于快速求解矩阵中某个子矩形区域的元素之和。
-
构造过程: 构造二维前缀和矩阵时,第(i, j)位置存储的是矩阵从左上角 (0,0) 到当前点 (i,j) 所有元素的和。即:
通过动态规划公式计算:
最后一项是因为左上区域被重复计算。
-
求子矩形和: 对于矩形区域 (x1, y1) 到 (x2, y2),矩形和可以快速通过公式求得:
处理边界时特别注意。
-
常用场景:
- 图像处理:如统计某块区域的像素强度。
- 游戏开发:快速统计地图上某区域的资源分布。
- 数据挖掘:例如统计二维表格中某范围内数据的总和。
总结
前缀和和二维前缀和极大优化了区间和的查询速度,将原本需要线性或平方复杂度的计算降低到常数时间,非常适合高频查询的场景。同时,它们的思想还可以延展到更高维问题或与其他算法结合,广泛用于竞赛编程和实际开发中。
农田灌溉最大化作物产量问题 - MarsCode
回到这个问题,如果我们会普通的一维前缀和,我们很容易就可以想到,提前通过前缀和计算好每一行和每一列的和,然后枚举灌溉的行和列即可,需要特别注意的是一行和一列会有一个交叉点,那个点会被灌溉两次,我们需要减去其中一次灌溉的影响。 不过这个题目不给数据范围,为了保险起见我开了long long,总复杂度为 。
参考代码
int solution(int m, int n, vector<vector<int>>& a) {
// n 行 m 列
typedef long long ll;
// 计算每一行和每一列的总和
vector<ll> row(n, 0);
vector<ll> col(m, 0);
ll sum = 0;
for(int i = 0 ; i < n ; i ++)
for(int j = 0 ; j < m ; j ++) {
row[i] += a[i][j];
col[j] += a[i][j];
sum += a[i][j];
}
ll ans = 0;
// 枚举灌溉的行和列,注意减去相交点,因为不能灌溉两次
for(int i = 0 ; i < n ; i ++)
for(int j = 0 ; j < m ; j ++) {
ans = max(ans, sum + row[i] + col[j] - a[i][j]);
}
return ans;
}