【APIO2009】采油区域
分类讨论
题目大意:
给定一个 $n\times m$ 的图,每个格子有正整数权值。
现在要找 $3$ 块互不相交的 $k\times k$ 的区域,使得 $3$ 块区域的权值和最大。求这个最大的权值和。
题解:
选 $3$ 块的方案只有以下 $6$ 种。
我们对这 $6$ 种情况分类讨论即可。
最后两种可以递推。令 $f_{i,t}$ 表示当前选到第 $i$ 行(列),选了 $t$ 块的最大权值和。
剩下的 $4$ 种,我们分别求出每个左上角、右上角、左下角、右下角、左侧、右侧、上侧、下侧的最大值。然后对于每一种情况,我们枚举中间的交点,然后分为两个角落和一个侧面,可以直接计算。
计算一个矩形区域的和,通过二维前缀和预处理,做到 $O(1)$ 查询。
时间复杂度 $O(nm)$。
Code:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1555;
int n,m,k,a[N][N],pre[N][N],ans;
int LU[N][N],LD[N][N],RU[N][N],RD[N][N],L[N],R[N],U[N],D[N];
int P[N][4];
inline int calc(int r1,int c1,int r2,int c2){
return pre[r2][c2]-pre[r1-1][c2]-pre[r2][c1-1]+pre[r1-1][c1-1];
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>k;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
cin>>a[i][j];
pre[i][j]=a[i][j]+pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1];
}
for(int i=k;i<=n;++i)
for(int j=k;j<=m;++j)
LU[i][j]=max({LU[i-1][j],LU[i][j-1],calc(i-k+1,j-k+1,i,j)});
for(int i=n-k+1;i;--i)
for(int j=k;j<=m;++j)
LD[i][j]=max({LD[i+1][j],LD[i][j-1],calc(i,j-k+1,i+k-1,j)});
for(int i=k;i<=n;++i)
for(int j=m-k+1;j;--j)
RU[i][j]=max({RU[i-1][j],RU[i][j+1],calc(i-k+1,j,i,j+k-1)});
for(int i=n-k+1;i;--i)
for(int j=m-k+1;j;--j)
RD[i][j]=max({RD[i+1][j],RD[i][j+1],calc(i,j,i+k-1,j+k-1)});
for(int j=1;j<=m;++j)
L[j]=LU[n][j],R[j]=RU[n][j];
for(int i=1;i<=n;++i)
U[i]=LU[i][m],D[i]=LD[i][m];
for(int i=k;i<=n-k+1;++i)
for(int j=k;j<=m-k+1;++j)
ans=max({ans,D[i]+LU[i-1][j]+RU[i-1][j+1],U[i]+LD[i+1][j]+RD[i+1][j+1],
L[j]+RU[i][j+1]+RD[i+1][j+1],R[j]+LU[i][j-1]+LD[i+1][j-1]});
memset(P,-1,sizeof P);
P[0][0]=0;
for(int j=1;j<=m;++j){
P[j][0]=0;
if(j>=k)
for(int t=1;t<=3;++t){
int&s=P[j][t];
P[j][t]=P[j-1][t];
if(P[j-k][t-1]>=0)
for(int i=k;i<=n;++i)
s=max(s,P[j-k][t-1]+calc(i-k+1,j-k+1,i,j));
}
}
ans=max(ans,P[m][3]);
memset(P,-1,sizeof P);
P[0][0]=0;
for(int i=1;i<=n;++i){
P[i][0]=0;
if(i>=k)
for(int t=1;t<=3;++t){
int&s=P[i][t];
P[i][t]=P[i-1][t];
if(P[i-k][t-1]>=0)
for(int j=k;j<=m;++j)
s=max(s,P[i-k][t-1]+calc(i-k+1,j-k+1,i,j));
}
}
ans=max(ans,P[n][3]);
cout<<ans<<endl;
return 0;
}