【算法竞赛刷题模板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] =
【算法竞赛刷题模板15】【二维数组前缀和】_第1张图片
s[i][j] =
【算法竞赛刷题模板15】【二维数组前缀和】_第2张图片
s[i-1][j] =
【算法竞赛刷题模板15】【二维数组前缀和】_第3张图片
s[i-1][j-1]=
【算法竞赛刷题模板15】【二维数组前缀和】_第4张图片
val[i][j]=
【算法竞赛刷题模板15】【二维数组前缀和】_第5张图片

综合表述,也就是下面这个样子:
【算法竞赛刷题模板15】【二维数组前缀和】_第6张图片
棕色是绿色和紫色的重合部分。

  • 在得到前缀和的基础上,针对题目要求适当变形即可

3.代码

#include
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

你可能感兴趣的:(#,other,#,络谷)