[总结] 二维ST表及其优化
二维 \(\mathcal{ST}\) 表,可以解决二维 \(\mathcal{RMQ}\) 问题。这里不能带修改,如果要修改,就需要二维线段树解决了。
上一道例题吧 ZOJ2859
类比一维 \(\mathcal{ST}\) 表,我们定义数组 \(f[i][j][k][p]\) 表示从 \((i,j)\) 往下 \(2^k\) 个元素,往右 \(2^p\) 个元素的最值。
建表的话,同样类比一维 \(\mathcal{ST}\) 表,外层两个循环 \(\mathcal{k}\) 和 \(\mathcal{p}\) , 然后内层取最值就行了。要注意的是,\(\mathcal{k}\) 和 \(\mathcal{p}\) 要从 \(0\) 开始循环,因为一行或者一列的情况也要维护。
查询的话,就把一个大矩形分成四个小矩形覆盖住就好了。
空间复杂度 \(\mathcal{O(n^2log^2n)}\)代码在这里
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #define N 302 int n,T; int val ; int f [9][9]; void prework(){ for(int i=0;i<9;i++){ for(int j=0;j<9;j++){ if(i==0 and j==0) continue; for(int k=1;k<=n-(1<<i)+1;k++){ for(int p=1;p<=n-(1<<j)+1;p++){ if(i==0) f[k][p]我们有一种把空间复杂度优化到 \(\mathcal {O(n^2logn)}\) 的方法,记 \(\mathcal{f[i][j][k]}\) 表示以点 \((i,j)\) 为左上角,边长为 \(\mathcal{2^k}\) 的正方形所要维护的最值。[i][j]=std::min(f[k][p][i][j-1],f[k][p+(1<<j-1)][i][j-1]); else f[k][p][i][j]=std::min(f[k][p][i-1][j],f[k+(1<<i-1)][p][i-1][j]); } } } } } int query(int r1,int c1,int r2,int c2){ int k1=log2(r2-r1+1); int k2=log2(c2-c1+1); return std::min(f[r1][c1][k1][k2],std::min(f[r2-(1<<k1)+1][c1][k1][k2],std::min(f[r1][c2-(1<<k2)+1][k1][k2],f[r2-(1<<k1)+1][c2-(1<<k2)+1][k1][k2]))); } void file(){ freopen("in.txt","r",stdin); freopen("out2.txt","w",stdout); } signed main(){ //file(); scanf("%d",&T); while(T--){ memset(f,0x3f,sizeof f); scanf("%d",&n); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) scanf("%d",&val[i][j]),f[i][j][0][0]=val[i][j]; } prework(); int q; scanf("%d",&q); while(q--){ int r1,r2,c1,c2; scanf("%d%d%d%d",&r1,&c1,&r2,&c2); printf("%d\n",query(r1,c1,r2,c2)); } } }
考虑查询,设查询矩形的左上角和右下角坐标分别为 \((r1,c1)\) 和 \((r2,c2)\)。且假设 \(r2-r1>c2-c1\)。
因为我们维护的是一个正方形内的最值,所以不能 \(\mathcal{O(1)}\) 的查询。而是要这样
for(int i=r1;i<=r2-(1<<k1)+1;i++) ans=min(ans,min(f[c1][i][k1],f[c2-(1<<k1)][i][k1]))
其实这样是能被一个宽度为 \(1\) 的长方形把查询复杂度卡成 \(O(n)\) 的,但毕竟空间复杂度小了一个 \(\mathcal{log}\) 倍,对于一些内存紧张的题目,这种做法还是能起到一定效果的。
下面是 \(\mathcal{ZOJ}\) \(2859\) 第二种做法的代码。
上一下两种方法的对比吧,大家自行比较选择。
第一种:
第二种:
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #define N 302 int T; int n; int val ; int f [9]; void prework(){ for(int i=1;i<9;i++){ for(int k=1;k<=n-(1<<i)+1;k++){ for(int p=1;p<=n-(1<<i)+1;p++){ f[k][p][i]=std::min(f[k][p][i-1],std::min(f[k+(1<<i-1)][p][i-1],std::min(f[k][p+(1<<i-1)][i-1],f[k+(1<<i-1)][p+(1<<i-1)][i-1]))); } } } } int query(int r1,int c1,int r2,int c2){ int k1=log2(r2-r1+1); int k2=log2(c2-c1+1); if(k1==k2) return std::min(f[r1][c1][k1],std::min(f[r2-(1<<k1)+1][c1][k1],std::min(f[r1][c2-(1<<k1)+1][k1],f[r2-(1<<k1)+1][c2-(1<<k1)+1][k1]))); if(k1<k2){ int minp=0x3f3f3f3f; for(int i=c1;i<=c2-(1<<k1)+1;i+=(1<<k1)) minp=std::min(minp,std::min(f[r1][i][k1],f[r2-(1<<k1)+1][i][k1])); minp=std::min(minp,std::min(f[r1][c2-(1<<k1)+1][k1],f[r2-(1<<k1)+1][c2-(1<<k1)+1][k1])); return minp; } int minp=0x3f3f3f3f; for(int i=r1;i<=r2-(1<<k2)+1;i+=(1<<k2)) minp=std::min(minp,std::min(f[i][c1][k2],f[i][c2-(1<<k2)+1][k2])); minp=std::min(minp,std::min(f[r2-(1<<k2)+1][c1][k2],f[r2-(1<<k2)+1][c2-(1<<k2)+1][k2])); return minp; } void file(){ freopen("in.txt","r",stdin); freopen("out1.txt","w",stdout); } signed main(){ //file(); scanf("%d",&T); while(T--){ memset(f,0x3f,sizeof f); scanf("%d",&n); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) scanf("%d",&val[i][j]),f[i][j][0]=val[i][j]; } prework(); /*for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ for(int k=0;k<9;k++) printf("i=%d,j=%d,k=%d,f=%d\n",i,j,k,f[i][j][k]); } }*/ int q; scanf("%d",&q); while(q--){ int r1,r2,c1,c2; scanf("%d%d%d%d",&r1,&c1,&r2,&c2); printf("%d\n",query(r1,c1,r2,c2)); } } return 0; }
- IT第七天 - 类及其属性、方法的理解,断点调试初识,代码优化总结,编程逻辑培养
- [置顶] 快速排序及其优化过程总结
- 最大子序列和问题 二维最大子序列核问题及其优化
- opencv学习:二维浮点数离散傅里叶变换及其扩展边界优化
- 动态规划3 背包总结及其优化
- [置顶] 递归与分治策略-2.7归并排序及其优化总结
- 学习总结 java 创建及其练习
- 数据库SQL优化大总结
- mybatis 学习总结三 优化配置
- 程序优化-基本优化(我的总结)
- 数据库查询优化方案的总结
- nio socket 及其开源框架MINA学习总结(二)
- 数据库常用SQL优化总结
- web前端优化总结
- Hive 查询优化总结
- CRM的dev(总结)--多思考,先实现,再优化!
- String和StringBuffer——字符串性能优化总结
- 35 个 Java 代码性能优化总结 10-20
- 35 个 Java 代码性能优化总结
- 【经验总结】网站前端性能优化总结