【算法竞赛刷题模板15】【二维数组前缀和】
【算法竞赛刷题模板15】【二维数组前缀和】
0.总结
Get to the points first. The article comes from LawsonAbs!
- 二维数组前缀和
- 容斥原理
下面结合一道例题【洛谷】P2280 [HNOI2003]激光炸弹,来讲讲如何实现二维数组的前缀和。
1.题意
二维坐标中每个点都有一定的价值。求出坐标系中以某点作为边长m的正方形的右下角的最大价值。
2.思想
二维数组前缀和
- 这里的前缀指的是从
(0,0)到(x,y)这个矩形区域,其和就是s[x][y] - 前缀和的推导公式
s[i][j] = s[i-1][j] + s[i][j-1]- s[i-1][j-1] + val[i][j],其中s[0][j]和s[i][0]需要单独判断
如果用图形表示的话,则是下面这个样子
s[i][j] =

s[i][j] =

s[i-1][j] =

s[i-1][j-1]=

val[i][j]=

综合表述,也就是下面这个样子:

棕色是绿色和紫色的重合部分。
- 在得到前缀和的基础上,针对题目要求适当变形即可
3.代码
#include<iostream>
using namespace std;
const int X = 5005,Y = 5005,N = 5005;
int s[N][N];//二维数组前缀和
int res = 0;
int main(){
int n,m;
cin >> n>> m;
int a,b,w,mx=0,my=0;
for(int i =0;i < n;i++){
cin >> a >> b >> w ;
mx=max(mx,a);//求出坐标的最大值
my=max(my,b);
s[a][b]+=w;//为节省空间,直接累加
}
//1.计算前缀和
//01.单独计算s[0][j], s[i][0]
//02.计算s[i][j]
for(int i = 1;i<= max(mx,my);i++){
s[i][0] = s[i-1][0] + s[i][0];
s[0][i] =s[0][i-1] + s[0][i];
}
for(int i= 1;i<=max(mx,my);i++){
for(int j = 1;j<=max(mx,my);j++){
//因为s[i][j]尚未计算过,所以可以累加
s[i][j] += s[i-1][j] + s[i][j-1] - s[i-1][j-1] ;
}
}
//2.计算m个正方形内的和,以(i,j)为正方形的右下角
//01.如果枚举左上角,则需要特判边界处理,所以更优的方法是枚举右下角
//02.注意这里的i,j的上限是max(mx,my),否则当m>mx时,就会出现问题。
for(int i = m-1;i<=max(mx,my);i++){
for(int j = m-1;j<=max(mx,my);j++){
int temp = s[i][j]
- (j-m<0 ? 0: s[i][j-m]) //防止下标越界&简洁,用三元运算符
- (i-m<0 ? 0: s[i-m][j])
+ ((i-m<0 || j-m<0)? 0:s[i-m][j-m]);
res = max(res,temp);
}
}
cout << res <<"\n";
}
测试用例
1 3
0 1 0
2 1
0 0 1
1 1 1
2 1
0 0 1
1 2 3
3 1
0 0 1
1 2 3
0 1 4
3 2
0 0 1
1 2 3
0 1 4