【BashuOJ2041】最大矩形-矩阵型DP

测试地址:最大矩形
题目大意:给定一个元素绝对值不超过10的矩阵,要求找出两个不相交的子矩阵,使得这两个子矩阵中元素之和的乘积最大。
做法:本题是一道矩阵型DP题。
两个不相交的子矩阵,要么被一条横向的分割线分隔,要么被一条纵向的分割线分隔。因此,我们枚举这条分隔线,然后在两边找到最大的子矩阵乘起来就可以了。
是这样吗?注意到,元素之和可能为负数,而负负得正,因此还要记录一个最小的子矩阵,来将其跟最大子矩阵乘积比较来得到最终的结果。
那么我们要怎么找到一个区域内最大和最小的子矩阵呢?事实上,我们可以枚举子矩阵的上下边界,然后中间的东西一列列分别合并成一个元素,然后就是一个动态规划的经典问题——最大(最小)子段和。相信到了这里就不用我再多讲了吧……总的时间复杂度为O(nm2)
想一想我们后面的枚举需要预处理出什么:某一条分割线上(下、左、右)面的区域内的最大和最小的子矩阵元素和。那么我们可以在求子矩阵的时候,算出以某一条分割线为上(下、左、右)边界的最大、最小的子矩阵元素和,然后就可以O(n+m)算出要求的数组了。算法的总复杂度为立方级别,可以通过。
以下是本人代码:

#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;
}
posted @ 2017-09-13 19:56  Maxwei_wzj  阅读(202)  评论(0编辑  收藏  举报