• [总结] 二维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[N][N];
    int f[N][N][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][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
    ",query(r1,c1,r2,c2));
    		}
    	}
    }
    

    我们有一种把空间复杂度优化到 (mathcal {O(n^2logn)}) 的方法,记 (mathcal{f[i][j][k]}) 表示以点 ((i,j)) 为左上角,边长为 (mathcal{2^k}) 的正方形所要维护的最值。
    考虑查询,设查询矩形的左上角和右下角坐标分别为 ((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[N][N];
    int f[N][N][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
    ",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
    ",query(r1,c1,r2,c2));
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    3js深入
    01课js初接触;
    弥合对象/关系之间的鸿沟(一)
    Spiral Matrix——螺旋矩阵
    原来···是不是高手,看try cathch都能看出来···
    Web视频分享处理类库的设计
    每个开发人员现在应该下载的十种必备工具
    使用C#得到局域网内所有主机名,IP地址,MAC地址,使用C# 实现查看所有系统事件
    IIS 错误 :“NETWORK SERVICE does not have write access” 解决办法
    配置Url Remap时发生Parser Error的解决办法
  • 原文地址:https://www.cnblogs.com/YoungNeal/p/8995685.html
Copyright © 2020-2023  润新知