二维前缀和 模板

本文详细介绍了前缀和的概念,重点讲解了一维前缀和的计算方法以及其在一维数据中的应用,进而扩展到二维前缀和的定义,探讨了如何利用二维前缀和快速求解矩阵区域和。通过实例和公式演示了二维前缀和求解过程,并提供了二维前缀和在实际问题中的应用示例,如矩阵和的查询。
摘要由CSDN通过智能技术生成

一、介绍:

【一维前缀和】

一维前缀和顾名思义
就是一维的前缀和
前缀和是什么呢?
前缀和就是到目前为止全部的和是多少
一维就是单纯的一串数
他的前缀和就成了一维前缀和

【举例】

1 2 3 4 5 6


他的前缀和依次就是 1 3 6 10 15 21
i位置上的前缀和就是从第一个数到第i

个数全部数的和

这样就很显然的知道了什么是一维前缀和了吧?

【二维前缀和是什么】

二维前缀和顾名思义
就是二维的前缀和
二维很显然了
有x轴和y轴也就是一个面
这很显然

那二维前缀和中一个f[i][j]表示的意思就是
以(1,1)为左上角以(i,j)为右下角这个矩阵里面数的和
如图

f[i][j]表示的就是图中红色的部分

【二维前缀和怎么求】

这里先不说,先假设自己知道了每f[i][j]

二维前缀和是多少
(这个锅我背,因为一开始设计的问题,导致先解释的求矩阵值而没有解释求f[i][j]

的值,因这两个很相似所以说一个就过了,先说了求矩阵那这里再说求点就有点重复了我嫌麻烦,所二维前缀和点的值就在最后说一下吧,在已知求矩阵的情况下)

【二维前缀和求矩阵元素和】

二维前缀和可以用来干什么呢?
一维前缀和你可以用来O(1)求某个点的值
那么类比一下
二维前缀和也是可以用来求某个矩阵的值的

但是怎么来求呢?

就如图中
知道了两个点的位置和他们的二维前缀和
图中红色是左上角的那个点的二维前缀和
红色+黄色部分是右下角的那个点的二维前缀和
是不是可以用这个来求出他们之间的矩阵的和呢?
也就是这一部分:

图中黑色的部分就是我们要求的那个矩阵和
看到这里yy一下
是不是可以通过某些奇怪的方法求出黑色部分是多少?

D点表示的二维前缀和值是红色部分+两个黄色部分+黑色部分
A点表示的是红色部分
B点表示的是上面的黄色部分+红色部分
C点表示的是下面的黄色部分+红色部分

这样是不是发现有什么神奇的东西快要出现了
这里面只有D的前缀和里面包括黑色部分
只要减去D里面的哪两个黄色部分和红色部分是不是就剩下了我们要求的黑色部分了?
那怎么减去呢?
可以这样:
D - B - C + A
这就是二维前缀和最重要的部分了
化成二维数组的形式就是这样的

f[i][j]−f[i−1][j]−f[i][j−1]+f[i−1][j−1]

【为什么上文成立】

继续看上面那张图
由D-B-C+A到方程式这个很显然所以就不多说了
只要证明出D-B-C+A是正确的那就没有问题了
这个可以化成:
红色部分+上面的黄色部分+下面的黄色部分+黑色部分-上面的黄色部分-红色部分-下面的黄色部分-红色部分+红色部分
这样是不是很巧妙的就只剩下了黑色部分
所以成立

【补充 —— 二维前缀和怎么求】

这个可以类比上面求矩阵的思想
只是这个矩阵的右下角是(i,j),左上角也是(i,j)
就是一个11的矩阵
所以也是很好求的
但是上面是已知D,A,B,C求黑色部分
这里你只知道A,B,C和黑色部分
因为是一个1
1的矩阵吗
所以黑色部分就只有一个元素也就是(i,j)坐标上的那个元素值
所以就可以个加法变减法,减法变加法一个性质的
通过A,B,C和黑色部分来求出D

D点表示的二维前缀和值是红色部分+两个黄色部分+黑色部分
A点表示的是红色部分
B点表示的是上面的黄色部分+红色部分
C点表示的是下面的黄色部分+红色部分

所以D就可以等于B+C-D+黑色部分:
上面的黄色部分+红色部分+下面的黄色部分+红色部分-红色部分+黑色部分
=上面的黄色部分+红色部分+下面的黄色部分+黑色部分
刚好等于D
方程式为

f[i][j]=f[i−1][j]+f[i][j−1]−f[i−1][j−1]+a[i][j]

二、模板

//查询某个矩阵里面数的总和
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int a[maxn][maxn],sum[maxn][maxn];
int main(){
    int n,m,s;
    cin>>n>>m;//输入原矩阵
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>a[i][j];
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];//二维前缀和
        }
    }
    int k;
    cin>>k;//k次询问
    while(k--){
        int x1,y1,x2,y2;
        cin>>x1>>y1>>x2>>y2;//两个顶点坐标
        cout<<sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1]<<endl;//两个顶点之间的矩形的面积
        
    }
    return 0;
}

三、例题:

数字矩阵

描述

给出一个nxn的数字矩阵,要求计算这个数字矩阵中是否存在一个子矩阵,要求这个子矩阵的行和列的长度相同,同时这个子矩阵内所有数的和为s。如果存在,输出满足条件的矩阵的最小的边长,否则输出-1.

输入

首先,输入两个数字n和s,分别表示这个矩阵的大小以及一个给定的数字s(0<n<=1000,s<=1e9)

然后输入nn行,每行nn个数字,表示这个矩阵。数据保证矩阵中每个数字都在[1,1e9]

输出

如果存在满足题目要求的子矩阵,那么请输出所有满足的子矩阵中最小的矩阵的边长,否则输出-1.

输入样例 1 

3 4
1 1 3
1 1 4
1 2 3

输出样例 1

1

输入样例 2 

3 100
1 1 3
1 1 4
1 2 3

输出样例 2

-1

提示

样例1解释,给出的矩阵中存在两个满足条件的子矩阵,一个是长度为2的全1的方阵,另一个是长度为1的全4的方阵,这两个方阵的所有的数字的和都是4,显然长度为1的方阵就是我们要找的,答案输出1.

样例2解释,题目中不存在满足条件的方阵,所以输出-1

思路

题目中要我们求出一个方阵的边长。这个方阵的所有的数的和要等于一个给定的值。做出这个题
目,你首先会使用一维前缀和。然后二维前缀和就是在这个的基础上实现的。这个自己可以结合几
何图形去进行理解。先前缀和处理之后,然后我们用双重 for 循环枚举右下角,对于左上角我们使
用二分枚举子矩阵的长度,我们发现固定了右下角的矩阵是存在单调性的,所以可以进行二分。这
里我一开始想的是直接对矩阵的长度进行二分,但是仔细想了一下发现这是不行的,首先我们假如
长度为 x 的存在符合条件的,并不能说明比这个更小或者更大的也不符合,这是没有单调性的。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[1010][1010],sum[1010][1010];
int n,s;
int main(){
    cin>>n>>s;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>a[i][j];
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
        }
    }
    int ans=0x3f3f3f3f;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            int l=0,r=min(i,j),mid;
            while(l<=r){
                mid=(l+r)>>1;
                ll total = sum[i][j]-sum[i-mid][j]-sum[i][j-mid]+sum[i-mid][j-mid];
                if(total>s){
                    r=mid-1;
                }
                else if(total<s){
                    l=mid+1;
                }
                else{
                    ans = min(ans,mid);
                    break;
                }
                
            }
        }
    }
    if(ans == 0x3f3f3f3f) cout<<-1<<endl;
    else cout<<ans<<endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值