纪念一下ccf csp 2021-4-11-2前缀和

二战了,

在这里插入图片描述

https://www.cnblogs.com/-Ackerman/p/11162651.html
大佬关于前缀和的介绍
【前缀和】

什么是前缀和?前缀和是一个数组的某项下标之前(包括此项元素)的所有数组元素的和。

设b[]为前缀和数组,a[]为原数组,根据这句话可以得到前缀和的定义式和递推式:

一维前缀和

定义式:
定义式子
递推式:
在这里插入图片描述

二维前缀和

定义式:
在这里插入图片描述
递推式:
在这里插入图片描述

【一维前缀和】

根据上面的定义,我们可以很容易得到 sum[i] = sum[i-1] + a[i]   这样就可以得到前i个数的和

根据上述表达式我们可以以O(1)求出区间[i,j]的区间和

sum[i,j]=b[j]-b[i-1]

【二维前缀和】

DP[i][j]表示(1,1)这个点与(i,j)这个点两个点分别为左上角和右下角所组成的矩阵内的数的和,好好想一下状态转移方程,DP[i][j]=DP[i-1][j]+DP[i][j-1]-DP[i-1][j-1]+map[i][j],怎么来的呢?我们画一下图就知道了。
在这里插入图片描述
这张图就知道了(i,j)可以由(i-1,j)和(i,j-1)两块构成,不过要注意两个点,1、有一块矩阵我们重复加了,也就是(i-1,j-1)这一块,所以我们要减去它。2、我们这个矩阵是不完整的,由图可知我们还有一块深蓝色的没有加,也就是(i,j)这一点,所以我们要再加上map[i][j]也就是题目给出的矩阵中这一格的数。

如果我们定义[x1,y1] 为所求矩阵左上角 [x2,y2] 为所求矩阵右下角 如何得到它们所围成矩阵的总和呢?
在这里插入图片描述

我们可以通过DP[x2][y2]来计算,我们通过图可以发现这个距离我们要的还差红色的部分看看怎么表示红色部分?我们可以分割成两块,分别是DP[x1][y2]和DP[x2][y1]我们发现有一块重复减了,所以我们再加上它即DP[x1][y1],有一点注意,因为画图和定义原因我们发现边界好像不对,我们来看看,我们定义的状态是整个矩阵包括边的和,而我们要求的也是要包括边的,所以我们要再改一下,把DP[x1][y2]和DP[x2][y1]和DP[x1][y1]分别改成DP[x1-1][y2]和DP[x2][y1-1]和DP[x1-1][y1-1]这样一减我们就可以得到自己想要的答案,整理可得公式,

DP[x2][y2]-DP[x1-1][y2]-DP[x2][y1-1]+DP[x1-1][y1-1]

这样我们就可以做到O(1)之内查询

求前缀和的代码:

#include<iostream>
#include<cstring>
using namespace std;
int dp[2000][2000],map[2000][2000];
int main()
{
    int m,n,k;//所给的矩阵是n*m的,有k组查询 
    cin >>n>>m>>k;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            cin >>map[i][j];
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++)//预处理一波 
        for(int j=1;j<=m;j++)
            dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+map[i][j];
    for(int i=1;i<=k;i++)//接受查询 
    {
        int x1,x2,y1,y2;
        cin >>x1>>y1>>x2>>y2;
        cout <<(dp[x2][y2]+dp[x1-1][y1-1]-dp[x1-1][y2]-dp[x2][y1-1])<<endl;//O(1)查询 
    }
    return 0;
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值