最近做题用到了这个,自己学习了一下,然后根据自己的理解写了这篇博客,如果有不对的地方,欢迎大家指正。
前缀和是一种预处理,可以降低时间复杂度,可以在后面的计算中可以直接应用前面已经算出的结果。
1.一维前缀和
这个主要是应用于在O(1)时间内求一个序列和。如给一个序列s,求s[i]+s[i+1]+s[i+2]+...s[n]。
算法十分简单,用数组sum[i]记录前n项序列s[i]的和(sum[i]=s[1]+s[2]+s[3]+...s[i]),s[n]=sum[n]-sum[i-1]。
例题:https://www.51nod.com/Challenge/Problem.html#!#problemId=1081
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
const int MAXN=50005;
long long q[MAXN];
long long s[MAXN];
int main()
{
long long n,t,x,y;
cin>>n;
s[0]=0;
for(int i=1;i<=n;i++)
{
cin>>q[i];
s[i]=s[i-1]+q[i];
}
cin>>t;
while(t--)
{
cin>>x>>y;
cout<<s[x+y-1]-s[x-1]<<endl;
}
return 0;
}
还可以用来求最大区间和。首先想到的肯定是暴力,时间复杂度变O(n^2),但是运用前缀和可以优化时间复杂度为O(n)。
int IntervalSum()
{
int MAXN=INF;
int MINN=-99999999;
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+a[i];
MINN=min(MINN,sum[i]);
MAXN=max(MAXN,sum[i]-MINN);
}
return MAXN;
}
for(int i = 1; i <= n; ++i)
{
sum += a[i];
if (sum < 0) sum = 0; //若小于0,就重新计数
ans = max(ans, sum);
}
2.二维前缀和
这个主要是应用于在O(1)时间内求一个矩阵和。
如给定一个矩阵s[i][j],sum[i][j]是矩阵s[1~i][1~j]的和。
则令sum[0][j]=sum[i][0]=0,
矩形和等于黑矩形面积加上蓝矩形面积减去重叠矩形面积再加上红矩形面积。
sum[i][j] = sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1] + a[i][j]
例题:http://acm.hdu.edu.cn/showproblem.php?pid=1559
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e3+5;
int s[MAXN][MAXN];
int dp[MAXN][MAXN];
int main()
{
int t,x,y,m,n;
cin>>t;
while(t--)
{
memset(dp,0,sizeof(dp));
memset(s,0,sizeof(s));
int number=0;
cin>>m>>n>>x>>y;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
cin>>s[i][j];
dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+s[i][j];
if(i>=x&&j>=y)
number=max(number,dp[i][j]+dp[i-x][j-y]-dp[i-x][j]-dp[i][j-y]);
}
}
cout<<number<<endl;
}
}
这种是动态规划的方法,感觉不太好理解,还可以降维。正常时间复杂度是O(n^4),但是运用前缀和可以优化时间复杂度为O(n^3)。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 4e2 + 5;
const int INF = 0x3f3f3f3f;
int n, ans = -INF;
int a[maxn][maxn], f[maxn][maxn]; //f[i][j]关于第j列到第i行的列前缀和
void init(const int& n)
{
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
f[i][j] = f[i - 1][j] + a[i][j]; //求一列的和
}
int b[maxn], sum[maxn]; //降维后的数组
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
scanf("%d", &a[i][j]);
init(n);
for(int i = 1; i <= n; ++i)
for(int j = i; j <= n; ++j)
{
for(int k = 1; k <= n; ++k) b[k] = f[j][k] - f[i - 1][k]; //降维
for(int k = 1; k <= n; ++k) sum[k] = sum[k - 1] + b[k]; //求一维前缀和
int Min = INF;
for(int k = 1; k <= n; ++k)
{
Min = min(Min, sum[k - 1]);
ans = max(ans, sum[k] - Min);
}
}
printf("%d\n", ans);
return 0;
}