每周一算法:二维前缀和

这篇具有很好参考价值的文章主要介绍了每周一算法:二维前缀和。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

二维前缀和

对一个序列预处理得到前缀和数组,可以在 O ( 1 ) O(1) O(1)的时间复杂度计算序列中任意区间的元素之和,这是前缀和算法的作用。而二维前缀和是用来优化处理子矩阵的和。

例如,对于矩阵 A = [ 1 4 5 2 9 5 2 1 6 9 8 3 4 2 1 6 ] A=\left[ \begin{matrix}1 & 4 & 5 & 2\\ 9 & 5 & 2 & 1 \\ 6 & 9 & 8 & 3 \\ 4 & 2 &1 &6\end{matrix} \right] A= 1964459252812136 中的一个子矩阵 [ 5 2 9 8 ] \left[ \begin{matrix} 5 & 2 \\ 9 & 8 \end{matrix} \right] [5928]的所有元素之和为 24 24 24

使用二维前缀和可以在 O ( 1 ) O(1) O(1)的时间复杂度计算出该子矩阵的和。

算法思想

构造前缀和数组

首先需要在原矩阵的基础上预处理一个二维前缀和数组 s [ ] [ ] s[][] s[][],其中 s [ i ] [ j ] s[i][j] s[i][j]表示从第 1 1 1行第 1 1 1列开始到第 i i i行第 j j j列所有数的和。

例如,对于矩阵 A = [ 1 4 5 2 9 5 2 1 6 9 8 3 4 2 1 6 ] A=\left[ \begin{matrix}1 & 4 & 5 & 2\\ 9 & 5 & 2 & 1 \\ 6 & 9 & 8 & 3 \\ 4 & 2 &1 &6\end{matrix} \right] A= 1964459252812136

  • s [ 1 ] [ 1 ] s[1][1] s[1][1]表示矩阵第 1 1 1行第 1 1 1列所有数的和,为 1 1 1
  • s [ 1 ] [ 2 ] s[1][2] s[1][2]表示矩阵第 1 1 1行第 1 1 1列到第 1 1 1行第 2 2 2列所有数的和,为 1 + 4 = 5 1+4=5 1+4=5
  • s [ 3 ] [ 3 ] s[3][3] s[3][3]该如何计算呢?

每周一算法:二维前缀和

分析下图可以发现, s [ 3 ] [ 3 ] s[3][3] s[3][3]由三部分组成

  • 绿色部分 s [ 2 ] [ 3 ] s[2][3] s[2][3]
  • 红色部分 s [ 3 ] [ 2 ] s[3][2] s[3][2]
  • 紫色部分 A [ 3 ] [ 3 ] A[3][3] A[3][3]

每周一算法:二维前缀和
由于 s [ 2 ] [ 2 ] s[2][2] s[2][2](黄色部分)被包含了两次,因此在计算 s [ 3 ] [ 3 ] s[3][3] s[3][3]时,需要去掉一个 s [ 2 ] [ 2 ] s[2][2] s[2][2]。因此, s [ 3 ] [ 3 ] = s [ 2 ] [ 3 ] + s [ 3 ] [ 2 ] − s [ 2 ] [ 2 ] + A [ 3 ] [ 3 ] s[3][3]=s[2][3]+s[3][2]-s[2][2]+A[3][3] s[3][3]=s[2][3]+s[3][2]s[2][2]+A[3][3]

那么,从第 1 1 1行第 1 1 1列开始到第 i i i行第 j j j列所有数的和 s [ i ] [ j ] = s [ i − 1 ] [ j ] + s [ i ] [ j − 1 ] − s [ i − 1 ] [ j − 1 ] + A [ i ] [ j ] s[i][j]=s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + A[i][j] s[i][j]=s[i1][j]+s[i][j1]s[i1][j1]+A[i][j]

每周一算法:二维前缀和

计算子矩阵的和

有了前缀和数组,如何计算矩阵 A = [ 1 4 5 2 9 5 2 1 6 9 8 3 4 2 1 6 ] A=\left[ \begin{matrix}1 & 4 & 5 & 2\\ 9 & 5 & 2 & 1 \\ 6 & 9 & 8 & 3 \\ 4 & 2 &1 &6\end{matrix} \right] A= 1964459252812136 中的一个子矩阵 [ 5 2 9 8 ] \left[ \begin{matrix} 5 & 2 \\ 9 & 8 \end{matrix} \right] [5928]的所有元素之和?

通过下图可以发现,从 ( 2 , 2 ) (2,2) (2,2) ( 3 , 3 ) (3,3) (3,3)的子矩阵之和可以由下面几个部分的和相减得到:

  • 天蓝色部分 s [ 3 ] [ 3 ] s[3][3] s[3][3]
  • 绿色部分 s [ 1 ] [ 3 ] s[1][3] s[1][3]
  • 红色部分 s [ 3 ] [ 1 ] s[3][1] s[3][1]

每周一算法:二维前缀和

由于黄色部分 s [ 1 ] [ 1 ] s[1][1] s[1][1]被减了两次,因此计算结果时需要补加一次 s [ 1 ] [ 1 ] s[1][1] s[1][1]。那么从 ( 2 , 2 ) (2,2) (2,2)开始到 ( 3 , 3 ) (3,3) (3,3)结束的子矩阵之和 = s [ 3 ] [ 3 ] − s [ 1 ] [ 3 ] − s [ 3 ] [ 1 ] + s [ 1 ] [ 1 ] =s[3][3]-s[1][3]-s[3][1]+s[1][1] =s[3][3]s[1][3]s[3][1]+s[1][1]

更一般地,从 ( x 1 , y 1 ) (x1,y1) (x1,y1)开始到 ( x 2 , y 2 ) (x2,y2) (x2,y2)结束的子矩阵之和 = s [ x 2 ] [ y 2 ] − s [ x 1 − 1 ] [ y 2 ] − s [ x 2 ] [ y 1 − 1 ] + s [ x 1 − 1 ] [ y 1 − 1 ] =s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1] =s[x2][y2]s[x11][y2]s[x2][y11]+s[x11][y11]
每周一算法:二维前缀和

算法实现

//构造前缀和数组
for(int i = 1; i <= n; i ++)
	for(int j = 1; j <= m; j ++)
	 	s[i][j] = s[i -1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
//输出(x1,y1)到(x2,y2)子矩阵的和
cout << s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] << endl;	 

时间复杂度

二维前缀和算法的时间复杂度分为两部分:

  • 构造二维前缀和数组: O ( n × m ) O(n\times m) O(n×m)
  • 求子矩阵的和: O ( 1 ) O(1) O(1)

空间复杂度

需要构造和原矩阵大小一样的前缀和数组,因此空间复杂度为 O ( n × m ) O(n\times m) O(n×m)


真题演练

洛谷P2004 领地选择

题目描述

作为在虚拟世界里统帅千军万马的领袖,小 Z 认为天时、地利、人和三者是缺一不可的,所以,谨慎地选择首都的位置对于小 Z 来说是非常重要的。

首都被认为是一个占地 C × C C\times C C×C 的正方形。小 Z 希望你寻找到一个合适的位置,使得首都所占领的位置的土地价值和最高。

输入格式

第一行三个整数 N , M , C N,M,C N,M,C,表示地图的宽和长以及首都的边长。

接下来 N N N 行每行 M M M 个整数,表示了地图上每个地块的价值。价值可能为负数。

输出格式

一行两个整数 X , Y X,Y X,Y,表示首都左上角的坐标。

样例输入

3 4 2
1 2 3 1
-1 9 0 2
2 0 1 1

样例输出

1 2

提示

对于 60 % 60\% 60% 的数据, N , M ≤ 50 N,M\le 50 N,M50

对于 90 % 90\% 90% 的数据, N , M ≤ 300 N,M\le 300 N,M300

对于 100 % 100\% 100% 的数据, 1 ≤ N , M ≤ 1 0 3 1\le N,M\le 10^3 1N,M103 1 ≤ C ≤ min ⁡ ( N , M ) 1\le C\le \min(N,M) 1Cmin(N,M)

解题思路

根据题目描述,在 N × M N\times M N×M的矩阵中求大小为 C × C C\times C C×C子矩阵的土地价值和的最大值,因此可以使用二维前缀和优化。文章来源地址https://www.toymoban.com/news/detail-461466.html

实现代码

#include <iostream>
using namespace std;
const int N = 1010;
long long s[N][N];
int n, m, c, x;
int main()
{
    cin >> n >> m >> c;
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
        {
            scanf("%d", &x);
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + x; 
        }
    long long ans = -1e10;
    int a, b;
    //枚举子矩阵的开始位置,注意子矩阵不能越界
    for(int x1 = 1; x1 + c - 1 <= n; x1 ++)
        for(int y1 = 1; y1 + c - 1 <= n; y1 ++)
        {
            int x2 = x1 + c - 1, y2 = y1 + c - 1;
            int t = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1];
            if(t > ans)
            {
                ans = t, a = x1, b = y1;
            }
        }
    cout << a << ' ' << b << endl;
}

总结

  • 二位前缀和可以优化求子矩阵的和
  • 构造二维前缀和数组:s[i][j] = s[i -1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
  • 使用前缀和数组求子矩阵的和:s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]

到了这里,关于每周一算法:二维前缀和的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [每周一更]-(第57期):用Docker、Docker-compose部署一个完整的前后端go+vue分离项目

    将公司物理机项目改为容器化部署,最终方案是通过容器docker-compose部署使项目可以ip+端口访问,再通过物理机nginx代理进行https域名访问。 可能还有更好的方式,开一个nginx的容器,进行代理,但由于跟物理机80,443端口冲突,暂时没有采用。 可能目前处理不是最好的方式,不

    2024年02月14日
    浏览(40)
  • [每周一更]-(第82期):认识自然处理语言(NLP)

    GPT的大火,带起了行业内大模型的爆发;国内外都开始拥有或者研发自己的大模型,下边我们从NLP来进一步深入了解大模型、AI。 一、什么是NLP? 自然语言处理 (英语:Natural Language Processing,缩写作 NLP )是人工智能和语言学领域的分支学科。此领域探讨如何处理及运用自然

    2024年01月16日
    浏览(42)
  • [每周一更]-(第69期):特殊及面试的GIT问题解析

    整合代码使用过程的问题,以及面试遇到的细节,汇总一些常用命令的对比解释和对比; 1、fetch和pull区别 git fetch是将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作本机分支中。 git pull则是将远程主机的最新内容拉下来后直接合并,即:git pull = git

    2024年02月08日
    浏览(43)
  • [每周一更]-(第54期):Go的多版本管理工具

    参考 https://zhuanlan.zhihu.com/p/611253641 https://learnku.com/articles/78326 前文概要 Go语言从开始使用从1.13起步,随着泛型的支持,带领团队在转型Go的时候,做基础组件架构选型使用1.18,但是Go版本不断迭代想使用最新版本来体验下,类比前端中node,有个nvm工具; 联想到Go应该也会有对

    2024年02月16日
    浏览(38)
  • [每周一更]-(第27期):HTTP压测工具之wrk

    [补充完善往期内容] wrk是一款简单的HTTP压测工具,托管在Github上,https://github.com/wg/wrk wrk 的一个很好的特性就是能用很少的线程压出很大的并发量. 原因是它使用了一些操作系统特定的高性能 io 机制, 比如 select, epoll, kqueue 等. 其实它是复用了 redis 的 ae 异步事件驱动框架. 确切的

    2024年02月03日
    浏览(37)
  • [每周一更]-(第45期):Docker私有镜像仓库配置并打通阿里云OSS

    Docker Registry 2 官方镜像创建一个私有镜像仓库,将Docker 镜像上传到 OSS 相应的路径中。 参考: BatchCompute Docker支持:https://help.aliyun.com/document_detail/143334.html?spm=a2c4g.143333.0.0.4a6f8752ls18FR Docker Registry:https://docs.docker.com/registry 基于OSS搭建私有 Docker Registry:https://developer.aliyun.com

    2024年02月03日
    浏览(43)
  • [每周一更]-(第83期):Go新项目-Gin中间件的使用和案例(10)

    在 Gin 中,中间件是一种用于处理 HTTP 请求和响应的功能强大的机制。中间件是一段位于请求处理链和最终处理器之间的代码, 它可以截获请求、执行预处理操作,修改请求或响应,然后将控制权传递给下一个中间件或最终的请求处理器。 中间件在业务使用中,方便注入一些

    2024年01月20日
    浏览(54)
  • 前缀和算法【一维、二维】

    首先这种算法适合于求 从 x 到 y 的和 。 一维代码十分简单,我们只需要每个都记录前面所有的和即可,注意细节 下标从1开始 这里我们就看两种情况:一种是 开始时 ,一种是 执行中 在开始时,因为我们是从1开始,a[0] = 0,所以第一个就是temp; 在执行过程中,因为前一个是

    2023年04月18日
    浏览(31)
  • 一、二维前缀和算法

    一维前缀和: 二维前缀和: 给你一个整数数组 nums ,请计算数组的 中心下标 。 数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。 如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中

    2024年02月16日
    浏览(36)
  • 【算法】激光炸弹(二维数组前缀和)

    地图上有 N 个目标,用整数 Xi,Yi 表示目标在地图上的位置,每个目标都有一个价值 Wi。 注意 :不同目标可能在同一位置。 现在有一种新型的激光炸弹,可以摧毁一个包含 R×R 个位置的正方形内的所有目标。 激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆

    2024年01月25日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包