枚举
一点都不会的暴枚题
把原地图用两条垂直于边界的直线分成三块,让三个正方形分别在这三块里取。那么可以分成六种情况:
对第一行的情况枚举其交点。对于第二行的情况枚举其中一条直线,另一条离枚举的这条距离为k。要预处理四个数组分别表示点 左上/右上/左下/右下内的最大K*K正方形权值。
具体实现见代码(BZOJ不能用读优我也不知道为什么):
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1505
#define F inline
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
int n,m,k,ans,a[N][N],b[N][N],c[N][N],d[N][N],w[N][N],s[N][N];
//F char readc(){
// static char buf[100000],*l=buf,*r=buf;
// if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
// return l==r?EOF:*l++;
//}
//F int _read(){
// int x=0; char ch=readc();
// while (!isdigit(ch)) ch=readc();
// while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
// return x;
//}
F int _read(){ int x; scanf("%d",&x); return x; }
F void Make(){
for (int i=k;i<=n;i++)
for (int j=k;j<=m;j++)
s[i][j]=w[i][j]-w[i-k][j]-w[i][j-k]+w[i-k][j-k];
for (int i=k;i<=n;i++)
for (int j=k;j<=m;j++)
a[i][j]=max(s[i][j],max(a[i-1][j],a[i][j-1]));
for (int i=k;i<=n;i++)
for (int j=m-k;j;j--)
b[i][j]=max(s[i][j+k],max(b[i-1][j],b[i][j+1]));
for (int i=n-k;i;i--)
for (int j=k;j<=m;j++)
c[i][j]=max(s[i+k][j],max(c[i+1][j],c[i][j-1]));
for (int i=n-k;i;i--)
for (int j=m-k;j;j--)
d[i][j]=max(s[i+k][j+k],max(d[i+1][j],d[i][j+1]));
}
int main(){
n=_read(),m=_read(),k=_read();
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
w[i][j]=w[i][j-1]+w[i-1][j]-w[i-1][j-1]+_read();
Make();
for (int i=k;i<=n-k;i++)
for (int j=k;j<=m-k;j++){
ans=max(ans,a[i][j]+b[i][j]+c[i][m]);
ans=max(ans,c[i][j]+d[i][j]+a[i][m]);
ans=max(ans,a[i][j]+c[i][j]+b[n][j]);
ans=max(ans,b[i][j]+d[i][j]+a[n][j]);
}
for (int i=k;i<=n-k;i++)
for (int j=k;j<=m;j++)
ans=max(ans,b[i][1]+d[i+k][1]+s[i+k][j]);
for (int j=k;j<=m-k;j++)
for (int i=k;i<=n;i++)
ans=max(ans,c[1][j]+d[1][j+k]+s[i][j+k]);
return printf("%d\n",ans),0;
}