zoukankan      html  css  js  c++  java
  • [总结] 二维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;
    }
    
  • 相关阅读:
    eclipse下c/cpp " undefined reference to " or "launch failed binary not found"问题
    blockdev 设置文件预读大小
    宝宝语录
    CentOS修改主机名(hostname)
    subprocess报No such file or directory
    用ldap方式访问AD域的的错误解释
    英特尔的VTd技术是什么?
    This virtual machine requires the VMware keyboard support driver which is not installed
    Linux内核的文件预读详细详解
    UNP总结 Chapter 26~29 线程、IP选项、原始套接字、数据链路访问
  • 原文地址:https://www.cnblogs.com/YoungNeal/p/8995685.html
Copyright © 2011-2022 走看看