修筑绿化带题解
修筑绿化带题解
我的做法实际上神奇且麻烦,大家其实可以看一下别人的做法。
这是一个单调队列优化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;
}