【BashuOJ2041】最大矩形-矩阵型DP
测试地址:最大矩形
题目大意:给定一个元素绝对值不超过10的矩阵,要求找出两个不相交的子矩阵,使得这两个子矩阵中元素之和的乘积最大。
做法:本题是一道矩阵型DP题。
两个不相交的子矩阵,要么被一条横向的分割线分隔,要么被一条纵向的分割线分隔。因此,我们枚举这条分隔线,然后在两边找到最大的子矩阵乘起来就可以了。
是这样吗?注意到,元素之和可能为负数,而负负得正,因此还要记录一个最小的子矩阵,来将其跟最大子矩阵乘积比较来得到最终的结果。
那么我们要怎么找到一个区域内最大和最小的子矩阵呢?事实上,我们可以枚举子矩阵的上下边界,然后中间的东西一列列分别合并成一个元素,然后就是一个动态规划的经典问题——最大(最小)子段和。相信到了这里就不用我再多讲了吧……总的时间复杂度为
想一想我们后面的枚举需要预处理出什么:某一条分割线上(下、左、右)面的区域内的最大和最小的子矩阵元素和。那么我们可以在求子矩阵的时候,算出以某一条分割线为上(下、左、右)边界的最大、最小的子矩阵元素和,然后就可以
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 1000000000
#define ll long long
using namespace std;
int n,m;
ll up[110][2],down[110][2],lft[110][2],rht[110][2];
ll a[110][110],sum[110],fmax[110],fmin[110],ans;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%lld",&a[i][j]);
for(int i=1;i<=n;i++)
{
up[i][0]=down[i][0]=inf;
up[i][1]=down[i][1]=-inf;
}
for(int i=1;i<=m;i++)
{
lft[i][0]=rht[i][0]=inf;
lft[i][1]=rht[i][1]=-inf;
}
for(int i=1;i<=n;i++)
{
memset(sum,0,sizeof(sum));
for(int j=i;j<=n;j++)
{
memset(fmax,0,sizeof(fmax));
memset(fmin,0,sizeof(fmin));
for(int k=1;k<=m;k++)
{
sum[k]+=a[j][k];
fmax[k]=max(fmax[k-1],(ll)0)+sum[k];
fmin[k]=min(fmin[k-1],(ll)0)+sum[k];
up[j][0]=min(fmin[k],up[j][0]);
up[j][1]=max(fmax[k],up[j][1]);
down[i][0]=min(fmin[k],down[i][0]);
down[i][1]=max(fmax[k],down[i][1]);
}
}
}
for(int i=1;i<=m;i++)
{
memset(sum,0,sizeof(sum));
for(int j=i;j<=m;j++)
{
memset(fmax,0,sizeof(fmax));
memset(fmin,0,sizeof(fmin));
for(int k=1;k<=n;k++)
{
sum[k]+=a[k][j];
fmax[k]=max(fmax[k-1],(ll)0)+sum[k];
fmin[k]=min(fmin[k-1],(ll)0)+sum[k];
lft[j][0]=min(fmin[k],lft[j][0]);
lft[j][1]=max(fmax[k],lft[j][1]);
rht[i][0]=min(fmin[k],rht[i][0]);
rht[i][1]=max(fmax[k],rht[i][1]);
}
}
}
for(int i=2;i<=n;i++)
{
up[i][0]=min(up[i][0],up[i-1][0]);
up[i][1]=max(up[i][1],up[i-1][1]);
}
for(int i=n-1;i>=1;i--)
{
down[i][0]=min(down[i][0],down[i+1][0]);
down[i][1]=max(down[i][1],down[i+1][1]);
}
for(int i=2;i<=m;i++)
{
lft[i][0]=min(lft[i][0],lft[i-1][0]);
lft[i][1]=max(lft[i][1],lft[i-1][1]);
}
for(int i=m-1;i>=1;i--)
{
rht[i][0]=min(rht[i][0],rht[i+1][0]);
rht[i][1]=max(rht[i][1],rht[i+1][1]);
}
ans=-inf;
for(int i=1;i<n;i++)
ans=max(ans,max(up[i][0]*down[i+1][0],up[i][1]*down[i+1][1]));
for(int i=1;i<m;i++)
ans=max(ans,max(lft[i][0]*rht[i+1][0],lft[i][1]*rht[i+1][1]));
printf("%lld",ans);
return 0;
}