修筑绿化带题解

修筑绿化带题解

我的做法实际上神奇且麻烦,大家其实可以看一下别人的做法。

这是一个单调队列优化DP题(废话)。

我的大概做法是:将每个c乘d的矩形化成一个点,将从a乘b的矩形中选一个c乘d的矩形转化为:在(a-c+1)*(b-d+1)的矩形中选一个值最小的点。

具体做法如下:

1.求出二维前缀和p,再求出以一个点为左上角的c乘d的矩形的值之和a。

2.求出每一以某点开始的长度为a-c+1的列的最小值sum。

3.剩下的就是在n-a+1行上,每一行求出长度为b-d+1的sum最小值(实际上就是每个a乘b的矩形中c乘d矩形的最小值),ans跟a乘b矩形权值和-最小值取max即可。

具体看代码吧:

#include<bits/stdc++.h>
using namespace std;
const int N=1006;
int n,m,t1,t2,t3,t4,t,head=1,tail=0,p[N][N],a[N][N],sum[N][N],q[N],ans=-1;
inline int read(){
   int T=0,F=1; char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-') F=-1; ch=getchar();}
   while(ch>='0'&&ch<='9') T=(T<<3)+(T<<1)+(ch-48),ch=getchar(); 
   return F*T; 
}
int main(){
    n=read(),m=read(),t1=read(),t2=read(),t3=read(),t4=read();
    for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) p[i][j]=read(),p[i][j]=p[i-1][j]+p[i][j-1]-p[i-1][j-1]+p[i][j];
    for(int i=1;i<=n-t3+1;++i) for(int j=1;j<=m-t4+1;++j) a[i][j]=p[i+t3-1][j+t4-1]-p[i+t3-1][j-1]+p[i-1][j-1]-p[i-1][j+t4-1];
    for(int i=1;i<=m-t4+1;++i){
        tail=0,head=1;
        for(int j=2;j<=t1-t3;++j){
            while(tail&&a[q[tail]][i]>=a[j][i]) --tail;
            q[++tail]=j;
        }
        for(int j=1;j<=n-t1+1;++j){ 
            sum[j][i]=a[q[head]][i];
            while(q[head]<=j+1&&head<=tail) ++head;
            while(tail>=head&&a[q[tail]][i]>=a[j+t1-t3][i]) --tail;
            q[++tail]=j+t1-t3;
        } 
    }
    for(int i=1;i<=n-t1+1;++i){
        head=1,tail=0;
        for(int j=2;j<=t2-t4;++j){
            while(tail&&sum[i][q[tail]]>=sum[i][j]) --tail;
            q[++tail]=j;
        }
        for(int j=1;j<=m-t2+1;++j){ 
            ans=max(ans,p[i-1][j-1]-p[i-1][j+t2-1]+p[i+t1-1][j+t2-1]-p[i+t1-1][j-1]-sum[i][q[head]]);
            while(q[head]<=j+1&&head<=tail) ++head;
            while(tail>=head&&sum[i][q[tail]]>=sum[i][j+t2-t4]) --tail;
            q[++tail]=j+t2-t4;
        } 
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2019-09-06 21:44  lsoi_ljk123  阅读(134)  评论(0编辑  收藏  举报