文章目录
前言、
什么是前缀和:例如一个数组:a[1],a[2],a[3]…a[n],前缀和S[i]表示的是该数组的前i项的和,例如S[3] = a[1] + a[2] + a[3],S[i] = a[1] + a[2] + a[3] + … + a[i - 1] + a[i].
一、关于前缀和
注:前缀和要求下标从1开始,可以避免下标的转换,对于a[0]的处理:赋值为0即可,从而S[1] = a[0] + a[1] = 0 + a[1] = a[1];S[2] = a[0] + a[1] + a[2] = 0 + a[1] + a[2] = a[1] + a[2]。对于S[N],a[N]数组可以定义为全局变量,这样a[0]初始值就为0
前缀和的作用:快速求出某段区间内元素的和
二、一维数组前缀和
1.求段区间前缀和
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]); //读入n个数
for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i]; //处理前缀和
2.例题:AcWing795. 前缀和
#include <cstdio>
using namespace std;
const int N = 100010;
int a[N], s[N];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i];
while (m -- )
{
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", s[r] - s[l - 1]);
}
return 0;
}
3、例题:蓝桥杯求和问题(间接用到前缀和的思想)
> 本题如果暴力肯定会超时,拆开来看很明显是前缀和
[链接描述](https://blog.csdn.net/WSY444/article/details/123454526?spm=1001.2014.3001.5501)
当n=4时
ans=a1*a2+a1*a3+a1*a4+a2*a3+a2*a4+a3*a4
=a1*(a2+a3+a4)+a2*(a3+a4)+a3*a4
就是 每个数 乘以 它之后的每个数之和 的和
至于区间的数之和就是前缀和了
一维前缀和
原数组: a0=0 a1 a2 a3 a4……an
前缀和数组:s0=0 s1 s2 s3 s4……sn (si=a0+a1+a2+...+ai)
(下标从1开始,并且定义s0=0是为了处理边界例如[1,x],并且保证求[l,r]公式s[r]-s[l-1]统一)
如何求si
for(i=1;i<=n;i++) s[i]=s[i-1]+ai;
求任意[l,r]的和:s[r]-s[l-1]
本题代码
#include<bits/stdc++.h>
using namespace std;
const int N = 200010;
int main() {
long long int n,b;
long long int sum=0;
cin>>n;
long long int a[N];
long long int s[N];
for(int i=1; i<=n; i++) { //i都从1开始,就会避免初始化的问题
cin>>a[i];
}
for(int i=1;i<=n;i++){
s[i]=s[i-1]+a[i];
} //求出了前缀和
for(int i=1;i<n;i++){
sum+=a[i]*(s[n]-s[i]);
}
cout<<sum;
return 0;
}
三、二维数组求前缀和
1.求S[i,j]
如图,用i表示行,j表示列:
根据图分析:求s[i, j]:
s[i, j] = s[i, j - 1] + s[i - 1, j] - s[i - 1, j - 1] + a[i, j];
2.求(x1,y1),(x2,y2)子矩阵的和
根据图分析:求s[x1 ~ x2, y1 ~ y2]:
s[x1 ~ x2, y1 ~ y2] = s[x2,y2] - s[x2, y1- 1] - s[x1 - 1, y2] + s[x1 - 1,y1 - 1];
3.例题:AcWing796. 子矩阵的和
#include <cstdio>
using namespace std;
const int N = 1010;
int a[N][N], s[N][N];
int main()
{
int n, m, q;
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
scanf("%d", &a[i][j]); //读入矩阵
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i- 1][j - 1] + a[i][j]; //计算每个s[i, j]
while (q -- )
{
int x1, x2, y1, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
printf("%d\n", s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]);//计算子矩阵
}
return 0;
}
4、例题2:二维前缀和
// 答案:12
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int n = 101;
int a[n][n];
void init() {
a[1][1] = 1;
// 构建初始矩阵
for (int i = 1; i < n; i++) {
for (int j = 1; j < n; j++) {
if (i > 1) a[i][j] = a[i - 1][j] + 1;
else if (j > 1) a[i][j] = a[i][j - 1] + 2;
}
}
// 构建前缀和
for (int i = 1; i < n; i++) {
for (int j = 1; j < n; j++) {
a[i][j] += a[i][j - 1] + a[i - 1][j] - a[i - 1][j - 1];
}
}
}
// 获取 [i][j] 到 [x][y] 之间子矩阵的和
int getSum(int i, int j, int x, int y) {
return a[x][y] - a[x][j - 1] - a[i - 1][y] + a[i - 1][j - 1];
}
signed main() {
init();
int ans = n * n;
for (int i = 1; i < n; i ++) {
for (int j = 1; j < n; j ++) {
for (int x = i; x < n; x ++) {
for (int y = j; y < n; y ++) {
int tmp = getSum(i, j, x, y);
if (tmp == 2022)
ans = min(ans, (x - i + 1) * (y - j + 1));
else if (tmp > 2022) // 超过 2022 可以剪枝
break;
}
}
}
}
cout << ans << endl;
return 0;
}
5、例题3:2024年春招美团软件开发笔试
小美的平衡矩阵
小美拿到了一个nn 的矩阵,其中每个元素是 0 或者 1。
小美认为一个矩形区域是完美的,当且仅当该区域内 0 的数量恰好等于 1 的数量。
现在,小美希望你回答有多少个ii的完美矩形区域。你需要回答1<=i<=n的所有答案。
输入描述
第一行输入一个正整数n,代表矩阵大小。
接下来的n行,每行输入一个长度为n的01 串,用来表示矩阵。
输出描述
输出n行,第i行输出的I*I 完美矩形区域的数量。
示例 1
输入
4
1010
0101
1100
0011
输出
0
7
0
1
思路与代码
二维前缀和。n只有200,所以直接枚举所有的边长i的正方形,判断正方形的和是否为i*i / 2即可。
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<vector<int>> matrix(n, vector<int>(n));
vector<vector<int>> prefix_sum(n + 1, vector<int>(n + 1));
// Read the matrix
for (int i = 0; i < n; ++i) {
string row;
cin >> row;
for (int j = 0; j < n; ++j) {
matrix[i][j] = row[j] - '0';
}
}
// Calculate prefix sum
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
prefix_sum[i][j] = prefix_sum[i - 1][j] + prefix_sum[i][j - 1] - prefix_sum[i - 1][j - 1] + matrix[i - 1][j - 1];
}
}
// Count perfect rectangles
for (int len = 1; len <= n; ++len) {
int cnt = 0;
for (int i = len; i <= n; ++i) {
for (int j = len; j <= n; ++j) {
int sum = prefix_sum[i][j] - prefix_sum[i - len][j] - prefix_sum[i][j - len] + prefix_sum[i - len][j - len];
if (sum == len * len / 2) cnt++;
}
}
cout << cnt << endl;
}
// int cnt[n+1] = {0};
// for (int i = 1; i < n; i ++) {
// for (int j = 1; j < n; j ++) {
// for (int x = i; x <= n; x ++) {
// for (int y = j; y <= n; y ++) {
// int sum = prefix_sum[x][y] - prefix_sum[i - 1][j] - prefix_sum[i][j - 1] + prefix_sum[i - 1][j - 1];
// if(sum*2 == x * x){
// cnt[i]++;
// }
// }
// }
// }
// }
return 0;
}