[前缀和]
前缀和是一种重要的预处理,能大大降低查询的时间复杂度。
最简单的一道题就是给定 n 个数和 m 次询问,每次询问一段区间的和。求一个 O(n + m) 的做法。
用 O(n) 前缀和预处理,O(m) 询问。
主要代码
1 for(int i = 1; i <= n; ++i)
sum[i] = sum[i - 1] + a[i]; //O(n)2 while(m--) //O(m) 3 { 4 int L, R; scanf("%d%d", &L, &R); 5 printf("%d\n", sum[R] - sum[L - 1]); 6 }
升级版
给定一个n*n的矩阵,找一个最大的子矩阵,使得这个子矩阵里面的元素和最大。
这道题最朴素的算法是 O(n ^ 6),用二维前缀和可以降到 O(n ^ 4)。
再想想前缀和矩阵怎么生成?看个图就明白了:
那么最大的矩形前缀和就等于蓝的矩阵加上绿的矩阵,再减去重叠面积,最后加上小方块,即
sum[i][j] = sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1] + a[i][j]
主要代码
1 for(int i = 1; i <= n; ++i) 2 for(int j = 1; j <= n; ++j) 3 sum[i][j] = sum[i - 1][j] + sum[i - 1][j] - sum[i - 1][j - 1] + a[i][j]; 4 for(int i = 1; i <= n; ++i) //枚举矩阵左右端点 5 for(int j= 1; j <= n; ++j) 6 for(int k = i; K <= n; ++k) 7 for(int h = j; h <= n; ++h) 8 { 9 tot = sum[k][h] - sum[i - 1][h] - sum[k][j - 1] + sum[i - 1][j - 1]; //同理矩阵生成 10 ans = max(ans, tot); 11 }
不过这道题还有另一种方法,可达到 O(n ^ 3),不过思路就和二维前缀和有些差别了。
为了更好理解,先降维做道题。
给定一个数字序列,求最大区间和。
其实这道题使用贪心的思想。因为 sum[L, R] = sum[R] - sum[L - 1],所以只要在枚举 R 的同时,一直取最小的 sum[L - 1],然后尝试着更新 ans。
这个代码实现有很多种方法,以下给出两种,都很好理解
第一种
1 for(int R = 1; R <= n; ++R) //已知s[R],找最小的s[L-1] 2 { 3 MIN = min(MIN, s[R - 1]); //就是求 sum[L] 4 ans = max(ans, s[R] - MIN); 5 }
第二种
1 for(int i = 1; i <= n; ++i) 2 { 3 sum += a[i]; 4 if (sum < 0) sum = 0; //若小于0,就重新计数 5 ans = max(ans, sum); 6 }
掌握了这两种方法,就可以解矩阵这道题了
只要枚举矩阵的上下边。
这里面的矩阵就可以延 i 到 j 的方向将维,再用上述思想更新答案
矩阵2减去矩阵1就得到矩阵3,然后将矩阵3降维。
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 const int maxn = 4e2 + 5; 8 const int INF = 0x3f3f3f3f; 9 int n, ans = -INF; 10 int a[maxn][maxn], f[maxn][maxn]; //f[i][j]关于第j列到第i行的列前缀和 11 void init(const int& n) 12 { 13 for(int i = 1; i <= n; ++i) 14 for(int j = 1; j <= n; ++j) 15 f[i][j] = f[i - 1][j] + a[i][j]; //求一列的和 16 } 17 int b[maxn], sum[maxn]; //降维后的数组 18 int main() 19 { 20 scanf("%d", &n); 21 for(int i = 1; i <= n; ++i) 22 for(int j = 1; j <= n; ++j) 23 scanf("%d", &a[i][j]); 24 init(n); 25 for(int i = 1; i <= n; ++i) 26 for(int j = i; j <= n; ++j) 27 { 28 for(int k = 1; k <= n; ++k) b[k] = f[j][k] - f[i - 1][k]; //降维 29 for(int k = 1; k <= n; ++k) sum[k] = sum[k - 1] + b[k]; //求一维前缀和 30 int Min = INF; 31 for(int k = 1; k <= n; ++k) 32 { 33 Min = min(Min, sum[k - 1]); 34 ans = max(ans, sum[k] - Min); 35 } 36 } 37 printf("%d\n", ans); 38 return 0; 39 }
总而言之,前缀和虽然大多数用来预处理,但要是出关于它的题也能出的很难。
__EOF__

本文链接:https://www.cnblogs.com/-Wind-/p/10162519.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话