数据结构与算法:二维前缀和、二维差分及离散化技巧

前言

有一维的前缀和以及差分当然有二维的~

一、二维前缀和

1.内容

二维前缀和就是求二维数组上从(0,0)位置到(i,j)位置的累加和。

2.模板——二维区域和检索 - 矩阵不可变

class NumMatrix {
public:vector<vector<int>>sum;NumMatrix(vector<vector<int>>& matrix) {int n=matrix.size();int m=matrix[0].size();sum.resize(n+2,vector<int>(m+2,0));for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){sum[i][j]=matrix[i-1][j-1];}}//二位前缀和for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){sum[i][j]+=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];}}}int sumRegion(int row1, int col1, int row2, int col2) {row1++;row2++;col1++;col2++;return sum[row2][col2]-sum[row2][col1-1]-sum[row1-1][col2]+sum[row1-1][col1-1];}
};

二维前缀和的求法就是,自己+左+上-左上。

此外,若想求点(a,b)到(c,d)区间上的累加和,求法就是在前缀和数组中,求(c,d)-(c,b-1)-(a-1,b)+(a-1,b-1)即可,具体原理就是一个容斥原理,大的矩形减去左边和上边两个小矩形,再加上左上角多减去一次的小矩形。

3.最大的以 1 为边界的正方形

class Solution {
public:int largest1BorderedSquare(vector<vector<int>>& grid) {int n=grid.size();int m=grid[0].size();build(n,m,grid);if(sum(0,0,n-1,m-1,grid)==0){return 0;}int ans=1;for(int a=0;a<n;a++){for(int b=0;b<m;b++){for(int c=a+ans,d=b+ans,k=ans+1;c<n&&d<m;c++,d++,k++){if(sum(a,b,c,d,grid)-sum(a+1,b+1,c-1,d-1,grid)==(k-1)<<2){ans=k;}}}}return ans*ans;}void build(int n,int m,vector<vector<int>>&grid){for(int i=0;i<n;i++){for(int j=0;j<m;j++){grid[i][j]+=get(i,j-1,grid)+get(i-1,j,grid)-get(i-1,j-1,grid);}}}int get(int i,int j,vector<vector<int>> &grid){return i<0||j<0?0:grid[i][j];}int sum(int a,int b,int c,int d,vector<vector<int>> &grid){return get(c,d,grid)-get(c,b-1,grid)-get(a-1,d,grid)+get(a-1,b-1,grid);}
};

从这个题开始就需要不少思维量和转化了。

判断是否被1包裹,思路是,构建二维前缀和数组,用外面大正方形的累加和减去除去边长里小正方形的累加和,若等于四倍的边长,就说明被1包裹。

这里注意是在原数组上操作,所以要判断下标是否越界。

二、二维差分

1.内容

没啥好说的,就是在二维数组上的差分。

2.模板——地毯

#include<bits/stdc++.h>
using namespace std;int get(int i,int j,vector<vector<int> >&grid)
{return i<0||j<0?0:grid[i][j];
}void build(int n,vector<vector<int> >&grid)
{for(int i=0;i<=n;i++){for(int j=0;j<=n;j++){grid[i][j]+=get(i,j-1,grid)+get(i-1,j,grid)-get(i-1,j-1,grid);}}
}void add(int a,int b,int c,int d,vector<vector<int> >&grid)
{grid[a][b]++;grid[a][d+1]--;grid[c+1][b]--;grid[c+1][d+1]++;
}void solve()
{int n,m;cin>>n>>m;vector<vector<int> >grid(n+2,vector<int>(n+2,0));for(int i=0,a,b,c,d;i<m;i++){cin>>a>>b>>c>>d;add(a,b,c,d,grid);}build(n,grid);for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){cout<<grid[i][j]<<" ";}cout<<endl;}
}int main()
{solve();return 0;
}

求二维差分的方法就是,点(a,b)和(c+1,d+1)上加上k,点(c+1,b)和(a,d+1)上减去k,之后对差分数组求一遍二维前缀和即可。

3.用邮票贴满网格图

class Solution {
public:int m,n;void build(vector<vector<int>>&sum){for(int i=1;i<=m;i++){for(int j=1;j<=n;j++){sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];}}}int sumArea(int a,int b,int c,int d,vector<vector<int>>&sum){return sum[c][d]-sum[c][b-1]-sum[a-1][d]+sum[a-1][b-1];}void add(int a,int b,int c,int d,vector<vector<int>>&diff){diff[a][b]++;diff[c+1][b]--;diff[a][d+1]--;diff[c+1][d+1]++;}bool possibleToStamp(vector<vector<int>>& grid, int stampHeight, int stampWidth) {m=grid.size();n=grid[0].size();vector<vector<int>>sum(m+1,vector<int>(n+1,0));for(int i=1;i<=m;i++){for(int j=1;j<=n;j++){sum[i][j]=grid[i-1][j-1];}}build(sum);vector<vector<int>>diff(m+2,vector<int>(n+2,0));for(int a=1;a<=m;a++){for(int b=1;b<=n;b++){int c=a+stampHeight-1,d=b+stampWidth-1;if(c<=m&&d<=n&&sumArea(a,b,c,d,sum)==0){add(a,b,c,d,diff);}}}build(diff);for(int i=1;i<=m;i++){for(int j=1;j<=n;j++){if(diff[i][j]==0&&grid[i-1][j-1]==0){return false;}}}return true;}   
};

 这个题主要是辅助数组的使用,在辅助数组上贴邮票,最后判断其和原数组是否都为0。

首先为了判断一个范围上是否能贴,即累加和为0,要先构建一个前缀和数组。之后根据前缀和数组里的信息进行贴邮票(能贴就贴),遍历每个(a,b)点,然后根据邮票大小计算(c,d),注意判断是否越界。最后遍历和原数组进行比较,若都是0就说明这里需要贴但没贴,返回false,否则true。

三、离散化技巧——最强祝福力场

class Solution {
public:typedef long long ll;int n;void add(int a,int b,int c,int d,vector<vector<ll>>&diff){diff[a][b]++;diff[c+1][b]--;diff[a][d+1]--;diff[c+1][d+1]++;}int build(vector<vector<ll>>&diff){int ans=0;for(int i=1;i<diff.size();i++){for(int j=1;j<diff[0].size();j++){diff[i][j]+=diff[i-1][j]+diff[i][j-1]-diff[i-1][j-1];ans=ans<diff[i][j]?diff[i][j]:ans;}}return ans;}int fieldOfGreatestBlessing(vector<vector<int>>& forceField) {n=forceField.size();vector<ll>xl(2*n);vector<ll>yl(2*n);for(ll i=0,j=0,k=0;i<n;i++){ll x=forceField[i][0];ll y=forceField[i][1];ll r=forceField[i][2];//转化成整数xl[j++]=2*x-r;xl[j++]=2*x+r;yl[k++]=2*y-r;yl[k++]=2*y+r;}//离散化->排序后去重sort(xl.begin(),xl.end());sort(yl.begin(),yl.end());map<ll,ll>mx;map<ll,ll>my;map<ll,ll>::iterator iter;for(int i=0,p=1;i<xl.size();i++){iter=mx.find(xl[i]);if(iter==mx.end()){mx[xl[i]]=p++;}}for(int i=0,p=1;i<yl.size();i++){iter=my.find(yl[i]);if(iter==my.end()){my[yl[i]]=p++;}}vector<vector<ll>>diff(xl.size()+2,vector<ll>(yl.size()+2,0));for(int i=0,a,b,c,d;i<n;i++){ll x=forceField[i][0];ll y=forceField[i][1];ll r=forceField[i][2];a=mx[2*x-r];b=my[2*y-r];c=mx[2*x+r];d=my[2*y+r];add(a,b,c,d,diff);}return build(diff);}
};

 在面对较大的数值,同时数值的大小本身又不重要时,可以对其进行离散化,节省空间。

首先,这个题的第一个难点是对小数的处理。因为在计算边长时会出现0.5这种小数,而根据题意,数值的大小本身并不重要,重要的是数值间的相对关系,所以考虑将其转化成整数。

再就是第二个难点,由于数值太大,不可能开一个这么大的数组,所以对其进行离散化。主要方法是,先将数组排序,之后去重,同时对其进行编号。之后,在进行差分时,只需要按编号个数构建数组,并按编号差分。这样不仅减少了数组大小,而且还不破坏力场的相对大小。这里也可以重新构建x和y数组,因为有序,所以每次用二分搜索找坐标对应的编号。

总结

确实有点烧脑了……

END

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/70801.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在 Vue 组件中,如何确认父组件在 add 模式下传入 value 的情况及其对子组件 getProducts() 方法的触发影响?

文章目录 父组件中 <ave-form> 的使用add 模式下触发逻辑value 的传入情况是否触发 getProducts()&#xff1f; 验证 add 模式下 getProducts() 是否触发结论&#xff1a; 检查父组件传入 value 的完整情况如何明确知道父组件传入的 value最终回答 父组件 index.vue子组件…

第十四届蓝桥杯Scratch11月stema选拔赛真题——小猫照镜子

编程实现&#xff1a; 小猫照镜子。(背景非源素材) 具体要求&#xff1a; 1). 运行程序&#xff0c;角色、背景如图所示&#xff1b; 完整题目可点击下方链接查看&#xff0c;支持在线编程~ 小猫照镜子_scratch_少儿编程题库学习中心-嗨信奥https://www.hixinao.com/tiku/s…

React + TypeScript 实现数据库逆向生成数据模型指南

React TypeScript 实现数据库逆向生成数据模型全栈指南 引言&#xff1a;逆向工程在现代开发中的价值 在微服务架构和快速迭代的背景下&#xff0c;数据库逆向生成数据模型已成为提升开发效率的核心技术。传统手动编写模型的方式存在模式同步延迟和类型安全缺失两大痛点。本文…

Android Audio实战——音频相关基础概念(附)

Android Audio 开发其实就是媒体源数字化的过程,通过将声波波形信号通过 ADC 转换成计算机支持的二进制的过程叫做音频采样 (Audio Sampling)。采样 (Sampling) 的核心是把连续的模拟信号转换成离散的数字信号。 一、声音的属性 1、响度 (Loudness) 响度是指人类可以感知到的…

小程序类目调整汇总公告

各位小程序开发者&#xff1a; 为进一步加强平台的规范管理&#xff0c;优化开发者类目选择体验&#xff0c;现对以下类目进行调整&#xff0c;请各位开发者知悉。 类目新增 非个人主体 #【交通服务-国际客运】 现资质要求 &#xff08;2选1&#xff09;&#xff1a; 1…

python的列表和元组别再傻傻分不清啦

目录 什么是下标&#xff1a; 正数索引&#xff1a;正数索引从左到右&#xff0c;从 0 开始。 负数索引&#xff1a;负数索引从右到左&#xff0c;从 -1 开始。 切片&#xff08;slice&#xff09;&#xff1a;除了单个元素&#xff0c;Python还支持通过切片访问序列的子集。…

dubbo转http方式调用

业务背景&#xff1a;在当前项目下&#xff0c;所有前端请求均通过外层网关转发到后端这边的dubbo服务&#xff0c;现计划去掉网关层&#xff0c;由前端直接http调用后端dubbo。 解决方案&#xff1a;在前端调用方式不变的前提下&#xff0c;后端服务新建controller层&#xf…

OpenHarmony构建系统实践-跨部件引用

上一篇通过gn构建系统利用部件构建了可执行程序、动态库和配置文件&#xff0c;以及部件内的引用&#xff0c;本篇通过实现跨部件的模块引用&#xff0c;通过实现部件间的使用方法&#xff0c;以此来达到复用三方部件和模块库的目的。 本节以实现两个自定义的部件为例&#xff…

在 compare-form.vue 中添加 compareDate 隐藏字段,并在提交时自动填入当前时间

在 compare-form.vue 中添加 compareDate 隐藏字段&#xff0c;并在提交时自动填入当前时间。 提交表单时存入的对象是FakeRegistration&#xff0c;这个对象里面有compareDate字段&#xff0c;刚好表格查询的对象也是FakeRegistration&#xff0c;所以表格展示的时间就是刚才…

Windows 11【1001问】如何安装Windows 11

紧接上篇内容&#xff0c;本文详细介绍了从准备工作到具体安装步骤的完整流程&#xff0c;帮助用户顺利完成Windows 11系统的安装。内容涵盖了ISO镜像文件的下载与校验、启动U盘的制作、硬件兼容性检查&#xff0c;以及BIOS/UEFI设置和系统安装过程中的关键步骤。通过逐步指导&…

Chromedriver与Chrome版本映射表

‌Chromedriver与Chrome版本映射表‌如下&#xff1a; ‌Chrome 71-73版本对应Chromedriver 2.46‌‌Chrome 70-72版本对应Chromedriver 2.45‌‌Chrome 69-71版本对应Chromedriver 2.44‌‌Chrome 68-70版本对应Chromedriver 2.43‌‌Chrome 67-69版本对应Chromedriver 2.42‌…

LSM-Tree (日志结构合并树)

LSM-Tree&#xff08;日志结构合并树&#xff09;是一种高效处理写操作的存储结构&#xff0c;广泛应用于NoSQL数据库如LevelDB和RocksDB。其核心思想是将随机写入转换为顺序写入&#xff0c;提升吞吐量。以下是其原理及Java实现示例&#xff1a; ### **LSM-Tree 原理** 1. **…

【玩转 Postman 接口测试与开发2_020】(完结篇)DIY 实战:随书示例 API 项目本地部署保姆级搭建教程(含完整调试过程)

《API Testing and Development with Postman》最新第二版封面 文章目录 最新版《Postman 接口测试与开发实战》示例 API 项目本地部署保姆级搭建教程1 前言2 准备工作3 具体部署3.1 将项目 Fork 到自己名下3.2 创建虚拟环境并安装依赖3.3 初始运行与项目调试 4 示例项目的用法…

3-提前结束训练

一、核心类 class EarlyStopping:# YOLOv5 simple early stopperdef __init__(self, patience30):self.best_fitness 0.0 # i.e. mAPself.best_epoch 0self.patience patience or float(inf) # epochs to wait after fitness stops improving to stopself.possible_stop …

若依 ruoyi-vue 根据角色切换路由菜单权限 SAAS

后端根据角色查询相应的菜单&#xff08;角色对应管理的系统&#xff09; /*** 获取路由信息根据角色&#xff08;系统类型&#xff09;** return 路由信息*/GetMapping("getRoutersBySystemType")public AjaxResult getRoutersBySystemType(String systemType) {Lon…

2024最新版鸿蒙纯血原生应用开发教程文档丨学习ArkTS语言-基本语法

ArkTS是HarmonyOS的主要应用开发语言&#xff0c;在TypeScript基础上进行了扩展&#xff0c;保留了其基本风格&#xff0c;并通过增强静态检查和分析来提高程序的稳定性和性能。本教程将帮助开发者掌握ArkTS的核心功能、语法及最佳实践&#xff0c;以便高效地构建高性能移动应用…

使用插件 `vue2-water-marker`添加全局水印

使用插件 vue2-water-marker添加全局水印 效果图 1、安装插件 npm install vue2-water-marker --save2、全局注册 // main.js import Vue from vue import Vue2WaterMarker from vue2-water-markerVue.use(Vue2WaterMarker)3、在组件中使用 <template><div id&q…

docker安装etcd:docker离线安装etcd、docker在线安装etcd、etcd镜像下载、etcd配置详解、etcd常用命令、安装常见问题总结

官方网站 官方网址&#xff1a;etcd 二进制包下载&#xff1a;Install | etcd GitHub社区项目&#xff1a;etcd-io GitHub GitHub社区项目版本历史&#xff1a;Releases etcd-io/etcd GitHub 一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令…

探索浮点数在内存中的存储(附带快速计算补码转十进制)

目录 一、浮点数在内存中的存储 1、常见的浮点数&#xff1a; 2、浮点数存储规则&#xff1a; 3、内存中无法精确存储&#xff1a; 4、移码与指数位E&#xff1a; 5、指数E的三种情况&#xff1a; 二、快速计算补码转十进制 1、第一种方法讨论&#xff1a; 2、第二种方…

第25周JavaSpringboot实战-电商项目 4.商品分类管理

商品分类模块开发笔记 模块功能概述 实现分类数据的 增删改查 功能核心难点&#xff1a; 分类的父子级目录结构递归实现多级分类查找列表展示顺序控制&#xff08;从父级向子级递归&#xff09; 接口说明 后台接口 1. 添加分类 请求地址: /admin/category/add 请求方法: …