zoukankan      html  css  js  c++  java
  • 【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;
    }
  • 相关阅读:
    学习总结javascript和ajax,php,和css
    css基础二
    JQUERY基础
    JS基础与DOM操作(一)
    div布局
    样式表
    框架及其他小技巧
    表格与表单
    HTML基础
    二阶段测试
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793587.html
Copyright © 2011-2022 走看看