前綴和


前綴和是一種重要的預處理,能大大降低查詢的時間復雜度。

最簡單的一道題就是給定 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 }

總而言之,前綴和雖然大多數用來預處理,但要是出關於它的題也能出的很難。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM