一维前缀和
题目
链接:一维前缀和模版题
思路分析
一:暴力O(q * N)
对于每一次询问,我们都可以用一个循环计算[l,r]区间内的元素和,
时间复杂度,O(q * N)
每一次计算一个区间都需要去循环一次,这是不是非常的麻烦呢?
我们能不能想一个办法把这个某个区间的和给存起来呢?进行反复利用呢?
所以,就优化出了,前缀和算法
二:前缀和O(q + N)
我们可以预处理出一个dp数组,来存放[1,i]的区间的元素和
当我们需要求区间[l,r]的元素和的时候,就可以用dp[r] - dp[l - 1]
这样每次查询的时候,时间复杂度就是O(1)那么我们怎么去预处理出这个dp数组呢?
我们可以用递推的思想,前n个元素的和等于前n-1个元素的和加上第n个元素的和
所以,dp[i[ = dp[i - 1] + arr[i]
细节:
这里的下标是从1,开始,如果从0开始需要对dp[0]特殊处理一下。
代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n ,q;
cin >> n >> q;
vector<int> a(n+1);
vector<long long> dp(n+1);
for(int i = 1;i < n + 1;i++)
{
cin >> a[i];
dp[i] = dp[i-1] + a[i];
}
while(q--)
{
int l,r;
cin >> l >> r;
cout << dp[r] - dp[l-1] << endl;
}
return 0;
}
// 64 位输出请用 printf("%lld")
二维前缀和
题目
链接:二维前缀和模版题
思路分析
暴力的时间复杂度是O(q * n2),很容易想,就不多叙述了。
直接讲一下二维前缀和算法的思路。
二维前缀和与一维前缀和类似,都是采用拼接的思路。
我们先预处理出来一个dp数组,用来存放从[1,1]到[i,j]这个矩阵内的元素和。
来,我们抽象出来一个矩阵,从[1,1]到[i,j]
如何去求这个区间的和呢?
很明显A + B + C + D对吧
但是B 和 C都不好求啊,
诶嘿,来,试试小学几何题常用的割补法。
我们可以将A+B看成一个整体,将A+C看成一个整体
那么A+B+C+D = (A+B)+(A+C)- A + D
具象到代码就是
dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + arr[i][j]
好,预处理完dp数组后,我们如何去使用dp数组去求任意一个矩阵的元素和呢?
假设我们就需要求D区间的元素和(假设左上顶点是x1,y1,右下顶点是x2,y2)
很容易想到的是,(A+B+C+D) - (A+B+C)
但是,和之前一样,B和C区间处理不了,依然采用割补法。
D = (A+B+C+D) - (A+B) - (A+C) + A
具象到代码就是
answer = dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1]
此时的时间复杂度是O(N2 + q)
细节:下标也是从1开始,dp数组多开一个空间,开到n+1,m+1
如果下标是从0开始,需要对边界情况进行特殊处理
代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n,m,q;
cin >>n >> m>> q;
vector<vector<int>> a(n+1,vector<int>(m+1));
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)
{
cin >> a[i][j];
dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + a[i][j];
}
}
while(q--)
{
int x1,y1,x2,y2;
cin >> x1 >> y1 >> x2 >>y2;
cout << dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1] << endl;
}
return 0;
}
国庆节结束了,我又回来啦,hhh,
继续更新!!!