天天看点

BZOJ1177 [Apio2009]Oil 二维前缀和 二维前缀最值

 ​​题目传送门 - BZOJ1177​​ 题意概括

  在一个n*m的矩阵中,每一个位置一个数字。

  现在让你选出3个k*k的矩阵,它们互不相交,问最大数值和为多少。

  注意:n,m<=1500

 题解

  一开始总想着dp,发现不大可能。

  暴搜也不行。

  然后突然发现,很简单,情况总数非常的少。

  只有以下6种,从3个区域中各选择一个最大的。

BZOJ1177 [Apio2009]Oil 二维前缀和 二维前缀最值

  然后就很简单了,我们只需要预处理矩阵前缀和,左上左下右上右下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;
}