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; }