2019牛客暑期多校第三场 F.Planting Trees (单调队列)

https://ac.nowcoder.com/acm/contest/883/F差点没搞死我

------------------------------------------------------------

题意:就是一个N*N的区域,每个格代表树高度,现在需要你去找一个区域植树,这个区域需满足区域内所有树的高度差小于m

题解:

  • 枚举上下左右边界O(N^4),时间复杂度爆炸,发现数据大小满足O(N^3)
  • 枚举上下边界,枚举右边界同时,维护两个单增/单减队列,来确定最大左边界, 单调队列确定左边界时就O(1)了
  • 再理解一下就是,对于枚举右边界确定的单增单减队列,可以很简便的确定左边界
  • 一定要好好理解这里  
  • mx[qmx[l2]]-mi[qmi[l1]]>m 左边界希望能越大,里右远,所以每次更新都更新成优先入队的内个位置
  •                            for(int k=1;k<=n;k++){//右边界 
    			        mx[k]=max(a[j][k],mx[k]);//最大值 
    			        mi[k]=min(a[j][k],mi[k]);//最小值 
    			        while(l1<r1&&mi[k]<=mi[qmi[r1-1]]) r1--; qmi[r1++]=k;
    				while(l2<r2&&mx[k]>=mx[qmx[r2-1]]) r2--; qmx[r2++]=k; 
    				while(l1<r1&&l2<r2&&mx[qmx[l2]]-mi[qmi[l1]]>m){//更新最小左边界                                 
                                         if(qmx[l2]<qmi[l1]) p=qmx[l2++];
    				      else p=qmi[l1++];
    				}
    				    area=max(area,(j-i+1)*(k-p));			  		   
    			    }
    #include<cstdio>
    #include<cstring>
    #include<string.h>
    #include<algorithm>
    #include<iostream>
    #include<stdlib.h>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int maxn=1000000;
    typedef long long ll;
    int a[600][600];
    int mx[600],mi[600];//列最大值,最小值
    int qmx[600],qmi[600]; //左边界最大,最小的位置,单调队列,o(1); 下标从0开始 
    int main(){
    	int t;
    	cin>>t;
    	while(t--){
    		int n,m;
    		cin>>n>>m;
    		int area=0;
    		for(int i=1;i<=n;i++){
    			for(int j=1;j<=n;j++)
    			   cin>>a[i][j];
    		}
    		for(int i=1;i<=n;i++){//上边界 
    		    memset(mx,0,sizeof(mx));
    		    memset(mi,inf,sizeof(mi));
    			for(int j=i;j<=n;j++){//下边界
    			    int l1=0,r1=0,l2=0,r2=0;
    			    int p=0;
    			    for(int k=1;k<=n;k++){//右边界 
    			        mx[k]=max(a[j][k],mx[k]);//最大值 
    			        mi[k]=min(a[j][k],mi[k]);//最小值 
    			        while(l1<r1&&mi[k]<=mi[qmi[r1-1]]) r1--; qmi[r1++]=k;
    				    while(l2<r2&&mx[k]>=mx[qmx[r2-1]]) r2--; qmx[r2++]=k; 
    				    while(l1<r1&&l2<r2&&mx[qmx[l2]]-mi[qmi[l1]]>m){//更新最小左边界 
    				  	    if(qmx[l2]<qmi[l1]) p=qmx[l2++];  
    				  	    else p=qmi[l1++];
    				    } //当这个最大值-最小值<=m才推出循环 
    				    area=max(area,(j-i+1)*(k-p));			  		   
    			    }
    			}
    		}
    		cout<<area<<endl;
    	}
    	
    	return 0;
    } 

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值