BZOJ1177 [Apio2009]Oil 二维前缀和 二维前缀最值
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ1177
题意概括
在一个n*m的矩阵中,每一个位置一个数字。
现在让你选出3个k*k的矩阵,它们互不相交,问最大数值和为多少。
注意:n,m<=1500
题解
一开始总想着dp,发现不大可能。
暴搜也不行。
然后突然发现,很简单,情况总数非常的少。
只有以下6种,从3个区域中各选择一个最大的。
然后就很简单了,我们只需要预处理矩阵前缀和,左上左下右上右下4个方向的前缀max。
然后对于前两种,分别枚举一下列号和行号;
对于后四种,只要枚举中间点就可以了。
所以复杂度为n2。可以过去了。
代码
#include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> using namespace std; const int N=1500+5; int n,m,k,nn,mm; int a[N][N],sum[N][N],val[N][N],Row[N],Col[N],LU[N][N],RU[N][N],LD[N][N],RD[N][N]; int main(){ scanf("%d%d%d",&n,&m,&k); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&a[i][j]); memset(sum,0,sizeof sum); memset(val,0,sizeof val); memset(Row,0,sizeof Row); memset(Col,0,sizeof Col); memset(LU,0,sizeof LU); memset(RU,0,sizeof RU); memset(LD,0,sizeof LD); memset(RD,0,sizeof RD); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) sum[i][j]=a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]; nn=n-k+1,mm=m-k+1; for (int i=1;i<=nn;i++) for (int j=1;j<=mm;j++) val[i][j]=sum[i+k-1][j+k-1]-sum[i-1][j+k-1]-sum[i+k-1][j-1]+sum[i-1][j-1]; for (int i=1;i<=nn;i++) for (int j=1;j<=mm;j++) Row[i]=max(Row[i],val[i][j]),Col[j]=max(Col[j],val[i][j]); for (int i=1;i<=nn;i++) for (int j=1;j<=mm;j++) LU[i][j]=max(val[i][j],max(LU[i-1][j],LU[i][j-1])); for (int i=1;i<=nn;i++) for (int j=mm;j>=1;j--) RU[i][j]=max(val[i][j],max(RU[i-1][j],RU[i][j+1])); for (int i=nn;i>=1;i--) for (int j=1;j<=mm;j++) LD[i][j]=max(val[i][j],max(LD[i+1][j],LD[i][j-1])); for (int i=nn;i>=1;i--) for (int j=mm;j>=1;j--) RD[i][j]=max(val[i][j],max(RD[i+1][j],RD[i][j+1])); int ans=0; /* ---- ---- ----- ----- ----- ----- |||| |--| | | | |---| | |-| |-| | ---- |--| ----- | | | ----- ----- ---- |---| ----- */ for (int i=1;i<=nn;i++){ int Max=0; for (int j=i+k;j<=nn;j++){ Max=max(Max,Row[j]); if (j+k>nn) break; ans=max(ans,LU[i][mm]+Max+LD[j+k][mm]); } } for (int i=1;i<=mm;i++){ int Max=0; for (int j=i+k;j<=mm;j++){ Max=max(Max,Col[j]); if (j+k>mm) break; ans=max(ans,LU[nn][i]+Max+RU[nn][j+k]); } } for (int i=k+1;i<=nn;i++) for (int j=k+1;j<=mm;j++){ int lu=LU[i-k][j-k],ru=RU[i-k][j],ld=LD[i][j-k],rd=RD[i][j]; ans=max(ans,lu+ru+LD[i][mm]); ans=max(ans,lu+ld+RU[nn][j]); ans=max(ans,ld+rd+LU[i-k][mm]); ans=max(ans,rd+ru+LU[nn][j-k]); } printf("%d",ans); return 0; }