二维前缀和简单总结

二维前缀和简单总结

注:这篇博客的两张图是我从别人的博客搬过来了的,这里把贴出这篇博客的链接讲的挺好的

什么是二维前缀和

首先我们要明白前缀和是个什么东西:“前缀和”是在一维数组中(假设该数组为 ar[] ,以ar[1] 作为第一个ar数组的第一个元素)说的,那么第i个元素的前缀和sum[i] = ar[1 ] +ar[2] + …+ar[i] ; 二维前缀和就是在二维数组中用的,既然说是二维,那么一定是一个矩形有边界的平面内的数字之和,例如下图可以看成一个二维数组形成的一个平面,(1 ,1)为二维数组的第一个元素下标,(i,j)是数组内任意一个元素的下标,则由这两个下标组成的矩形区域(红色区域)内所有的数字之和,就是二维前缀和(这里强调一定是,数组的原点(1,1)必须是矩形的右上顶点,这样矩形区域内的数组之和才叫二维前缀和
在这里插入图片描述


有啥用?

用处就是:用二维前缀和预处理过二维数组之后可以,可以以O(1)的时间求出在二维数组中的某个矩形(或正方形)的区域内数字之和(二维前缀和完美的体现了预处理的强大)。


具体实施步骤

1 .预处理二维数组(假设为map[][]

根据⬆️图:我们假设要求是(1,1)的(i,j)为定点到矩形区域到前缀和这里我们可以把前缀和单程面积来思考,
首先我们假设(1,1)到(i,j)组成的矩形区域面积(其实是前缀和)为S(i,j),
其次假设从(1,1)到(i - 1,j)区域的面积为S(i - 1 , j),
最后假设从(1,1)到(i , j - 1 )区域的面积为S(i , j - 1)。
另外,由于图中以(i - 1 ,j- 1)到(i, j)形成蓝色区域到面积就是map[ i][j].
这个时候我们通过拼凑的方法求出我想要的矩形区域的面积(其实是前缀和)
这样根据图我们很容易的推出:S(i , j) = S(i - 1 , j) + S(i , j - 1)- S( i - 1 , j - 1) +map[ i ][j]
在这个表达式中有两部分要注意:
第一部分: - S( i - 1 , j - 1)是因在在区域面积S(i - 1 , j) 与 S(i , j - 1) 重合的面积为 S( i - 1 , j - 1) (即图中的红色区域);
第二部分:+map[ i ][ j ] : 加上它是因为拼凑完面积之后还缺少图中蓝色区域所代表的面积,所以要加上 map[ i ][ j ] 所代表的蓝色区域的面积


2.实践求某个矩形/正方形区域内的数字和

在这里插入图片描述
1⃣️: 核心思想 : 还是拼凑出我们想求的区域的面积(这里的面积就是我们所求区域的数字和),通过之前的 预处理操作
举例子来说吧:例如我们想求上图中绿色区域的数字和 S直接拼凑就行了:S = S(x2, y2)- S(x2,y1) - S(x1, y2) + S(x1 , y1);
⚠️:这里的+ S(x1 , y1)是因为我们重复减了S(x1 , y1 )这个区域两次,所以要再加上一次这个区域。

2⃣️:有了上面那个表达式后,还需要对这个式子略微做一些变形,一般题目给我们的条件就是:所求区域的竖直方向的宽度为b 、水平方向的长度为a,那么我们根据已知的x1、y1、a、b,可以推出我们所需要的的x2、y2.
公式如下:

x1 + a = x2 、y1 + b = y2
将这两个表达式带入表达式S得:S = S(x1 + a , y1 + b) - S(x1 + a , y1) - S(x1 , y1 + b) + S(x1 , y1);

3⃣️:我们还缺最后一步就可以搞定了:由于画图的原因我们发现边界好想不对,而且表达S所求的区域也有点不对,让们稍微的调整一下S表达式,让它求的恰好是我们所需要求的二维区域 并且 所求的矩阵区域和包括矩形区域的边上的元素,其实这个改动非常简单,就是在表达式中S中的x1、y1 分别替换成x1 - 1 、 y1 - 1,所以最终我们想求区域的数字和的表达式为S = S(x1 - 1 + a , y1 - 1 + b) - S(x1 - 1 + a , y1- 1) - S(x1 - 1 , y1 - 1 + b) + S(x1 - 1 , y1 - 1 );


学以致用

最大子矩阵和传送门

题解如下

#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;

const int Len = 1005;
int map[Len][Len];            //存放数组元素
long long S[Len][Len];        //子矩阵和

int main()
{
//    freopen("T.txt","r",stdin);
    int t;
    scanf("%d", &t);
    while(t --)
    {
        memset(S , 0 , sizeof(S));
        int m,n,x,y;
        scanf("%d %d %d %d", &m, &n, &x, &y);
        for(int i = 1; i <= m; i ++)
            for(int j = 1; j <= n; j ++)
            {
                scanf("%d", &map[i][j]);
                //预处理二维前缀和
                S[i][j] = S[i - 1][j] + S[i][j - 1] - S[i - 1][j - 1] + map[i][j];
            }

        long long ans = -1;
        for(int i = 1; i <= m - x + 1; i ++)
            for(int j = 1; j <= n - y + 1; j ++)
                ans = max(ans , S[i + x - 1][j + y - 1] - S[i + x - 1][j - 1] - S[i - 1][j + y - 1] + S[i - 1][j - 1]);
                                    //max函数的第二个参数就是求给定长宽后的矩形区域的数字和
        printf("%lld\n",ans);
    }
    return 0;
}
发布了78 篇原创文章 · 获赞 109 · 访问量 5638

猜你喜欢

转载自blog.csdn.net/qq_34261446/article/details/104010466