目录
1.一维前缀和
原数列a:1 2 3 4 5 6 7 8 9 s[i]=a[1]+a[2]...+a[i]
前缀和数列s:1 3 6 10 15 21 28 36 45
对于求区间[L,R]的前缀和:
1.1 朴素方法(此时时间复杂度为O(n))
int main()
{
int sum=0;
for(int i=l;i<=R;i++)
sum+=a[i];
}
1.2 改进方法:
首先算好前缀和,然后利用result=s[R]-s[L-1]
#include<iostream>
using namespace std;
int n,m;//输入数列个数与查询的次数
const int N=1001;
int a[N],s[N];//定义原数列与前缀和数列
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=s[i-1]+a[i];//求前缀和
}
while(m--)
{
int L,R;
cin>>L>>R;
cout<<s[R]-s[L-1]<<endl;//利用前缀和求出来区间内的和
}
return 0;
}
2. 二维前缀和
2.1 方法
- 定义二维矩阵a
1 2 3 4
5 6 7 8
-
定义其前缀和矩阵sum
我们用数组sum[i][j]表示左上角a[1][1]到左下角a[i][j]的和,其中sum[3][4]即绿色部分的和
sum[i][j]的预处理:
你要算出黄绿蓝,三个部分的和,你要知道,当你再算sum[i][j]的时候已,即
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]
1 3 6 10
6 14 24 36
-
利用前缀和矩阵求a的任意子矩阵的和。我们开始来看一看一个任意的以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵怎么求:
首先这个矩阵的和肯定包括在sum[x2][y2]中,但是多了一些东西,所以我们要把蓝的,黄的,绿的部分减去,首先看如何减去蓝的,用最粗暴的方法,直接减掉sum[x1-1,y2],好现在只要减掉一个绿色部分就行了,依然用简单粗暴的办法,减掉sum[x2][y1-1],但是此时黄色被减去了两次,所以需要在加回去。因此子矩阵的和就等于sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1]
2.2 代码
#include<iostream>
#include<cstring>
using namespace std;
int a[2000][2000],sum[2000][2000];
int main()
{
int n,m,q;//所给的矩阵是n*m的,有q组查询
cin >>n>>m>>q;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin >>a[i][j];//输入原矩阵
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++)//前缀和
for(int j=1;j<=m;j++)
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
for(int i=1;i<=q;i++)//接受查询
{
int x1,x2,y1,y2;
cin >>x1>>y1>>x2>>y2;
cout <<(sum[x2][y2]+sum[x1-1][y1-1]-sum[x1-1][y2]-sum[x2][y1-1])<<endl;//O(1)查询
}
return 0;
}
2.3 例题
Sample Input
simple output
代码
#include<iostream>
using namespace std;
int s[5010][5010];//定义前缀和数组
int main()
{
int N, R;//N为目标数,R为能炸掉的正方形的边长
cin >> N >> R;
R = min(R, 5001);//地图范围一定,爆炸范围不需要很大,只需要考虑地图范围内即可
for (int i = 1; i <= N; i++)//目标的输入
{
int x, y, w;
cin >> x >> y >> w;
s[++x][++y] += w;
//x和y都可以取0,为了后面循环的方便,让他们都变到从1开始的位置
}
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 ans = 0;
//由于在R-1或者之前的位置会消耗一部分地图外的,是没有价值的,所以从R,R开始
for (int i = R; i <= 5001; i++)
for (int j = R; j <= 5001; j++)
ans = max(ans, s[i][j] - s[i - R][j] - s[i][j - R] + s[i - R][j - R]);
//求原来最大的与当前遍历到的RxR的正方形的所有价值总和的最大值
cout << ans;
return 0;
}
Input
Output
代码
#include<iostream>
using namespace std;
int main()
{
int a[100][100];
int n,m;
int q;
int u, d, l, r, value, ans;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> value;
a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + value;
}
}
cin >> q;
while (q--) {
cin >> u >> d >> l >> r;
ans = a[d][r] - a[d][l - 1] - a[u - 1][r] + a[u - 1][l - 1];
cout << ans << endl;
}
return 0;
}