一维数组的前缀和
暴力解法:每次输入 l、r 时,找到数组中下标为 l 的数,一直相加到下标为 r 的数,由于查询 q 次,所以时间复杂度为 O(n*q) 。如果 l、r 之间的数有一部分在之前已经算过,相当于重复计算了。
为了避免重复计算,我们可以定义一个长度为 n 的数组 dp,dp[ i ] 保存数组 v 从 0 ~ i 的数的和,也就是前缀和。如图,dp [ i ] 计算的是绿色框内数字的和,即 v[ 1 ] 到 v[ i ] 的和。
在计算 dp [ i ] 时 ,由于 dp[ i -1 ] 记录了 v[ 1 ] 到 v[ i -1 ] 的和,所以我们没必要再次从头到尾遍历 v 数组来计算 dp [ i ] ,我们直接 dp [ i ] = dp[ i -1 ] + v [ i ] 。
我们注意到题目中提供的数组是从 1 开始计数的,这是为了避免数组的越界访问。如果从 0 开始计数,那么 i == 0 时,i - 1 = -1,则我们需要特殊处理 dp[ i -1 ] ,避免越界访问;如果从 1 开始计数,我们把 dp[ 0 ] 初始化为 0, i == 1 时,计算 dp[ i -1 ] 就不用特殊处理了。因为是从 1 开始计数的,原本长度为 n 的数组,我们需要设置长度为 n+1 。
当输入 l、r 时,由于 dp[ l ] 计算的是v [ 1 ] ~ v[ l ] 的和, dp[ r ] 计算的是v [ 1 ] ~ v[ r ] 的和,v[ l ] ~ v[ r ] 的和应该为v [ 1 ] ~ v[ r ] 的和 减去 v [ 1 ] ~ v[ l-1 ] 的和,即 dp [ r ] - dp [ l-1 ] 。
#include <iostream>
using namespace std;
#include<vector>
int main() {
int n,q;
cin>>n>>q;
vector<int> v(n+1);
for(int i=1;i<n+1;i++)
{
cin>>v[i];//输入数组
}
vector<long long> dp(n+1);//前缀和
for(int i=1;i<n+1;i++)
{
dp[i]=dp[i-1]+v[i];
}
int l,r;
while(q--)
{
cin>>l>>r;
cout<<dp[r]-dp[l-1]<<endl;
}
return 0;
}
二维数组的前缀和
题目给定的二维数组中,输入 x1,y1,x2,y2,得出子矩阵的和,即下图中绿色框内的和。
暴力解法依旧是直接遍历给定范围的矩阵,这样的时间复杂度很高,会超时。
受一维数组的前缀和的启发,我们可以创建一个二维数组 dp,dp 中记录了以 vv[ 1 ][ 1 ]为左上角,vv[ i ][ j ]为右下角的矩阵的和。(矩阵从 1 开始计数,同样为了避免越界访问)
假设我们需要计算以 vv[ 1 ][ 1 ]为左上角,vv[ i ][ j ]为右下角的矩阵的和,我们可以把 vv[ i ][ j ]的和切割为求下图中矩阵A、B、C、D的和,但是 B、C的和不好求,因为又需要再遍历一遍数组,我们可以拼凑法,A+B 、A+C 的和已经计算过了,所以可以拼凑为:A+B+C+D = (A+B) + (A+C) - A + D,A+B 为 dp[ i-1 ][ j ],A+C 为 dp[ i ][ j -1 ],A 为 dp [ i-1 ][ j-1 ] ,
dp 矩阵里面的数为 long long 类型,防止多个 int 类型的数相加导致溢出。
dp[ i ][ j ] = dp[ i-1 ][ j ] + dp[ i ][ j-1 ] - dp[ i-1 ][ j-1 ] + vv[ i ][ j ];
计算完二维数组的前缀和后,开始查询 x1, y1, x2, y2 的矩阵的和。
我们需要计算 D 的矩阵的和,我们可以根据前缀和 dp,把 D 先拼凑成 A、B、C、D,由于 B、C 的矩阵和不方便计算,可以转换为便于计算的 A+B、A+C,
所以 D = A + B + C + D - ( A+B ) -( A+C ) + A
long long ret=dp[ x2 ][ y2 ] - dp[ x1-1 ][ y2 ] - dp[ x2 ][ y1-1 ] + dp[ x1-1 ][ y1-1 ];
#include <iostream>
using namespace std;
#include<vector>
int main() {
int n=0,m=0,q=0;
cin>>n>>m>>q;
vector<vector<int>> vv(n+1,vector<int>(m+1));//注意怎么创建二维矩阵
for(int i=1;i<n+1;i++)
{
for(int j=1;j<m+1;j++)
{
cin>>vv[i][j];
}
}
vector<vector<long long>> dp(n+1,vector<long long>(m+1));
for(int i=1;i<n+1;i++)
{
for(int j=1;j<m+1;j++)
{
dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+vv[i][j];
}
}
int x1=0,x2=0,y1=0,y2=0;
while(q--)
{
cin>>x1>>y1>>x2>>y2;
long long ret=dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1];
cout<<ret<<endl;
}
return 0;
}
// 64 位输出请用 printf("%lld")