【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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
vue、konva标注多边形和矩形是指在vue框架下使用konva库实现对多边形和矩形进行标注的一种解决方案。 在demo2.0中,我们可以首先使用vue-cli创建一个新的vue项目,并安装konva库。接下来,在项目中引入konva库,并创建一个konva舞台(Stage)和一个konva层(Layer),用于显示多边形和矩形。 对于多边形标注,我们可以监听用户的鼠标点击事件,并使用konva的Line组件来绘制多边形。当用户点击时,我们可以记录下鼠标的坐标,并将其添加到多边形的坐标数组中。同时,我们还可以在每次点击后重新绘制多边形,显示出用户已经绘制的线段。 对于矩形标注,我们可以监听用户的鼠标按下和抬起事件,并分别记录下按下和抬起时的鼠标坐标。在按下和抬起时,我们可以使用konva的Rect组件来绘制矩形,并根据按下和抬起的坐标计算出矩形的宽度和高度。 除了标注绘制外,我们还可以对绘制的多边形和矩形进行编辑和删除。例如,我们可以监听用户的鼠标双击事件,并根据鼠标的位置找到用户点击的多边形或矩形。然后,我们可以在选中的多边形或矩形上显示编辑手柄,并监听手柄的拖拽事件,实现对多边形或矩形的位置、大小的编辑。同时,我们可以提供一个删除按钮,用户点击后可以将选中的多边形或矩形删除。 总之,vue、konva标注多边形和矩形的demo2.0提供了一个方便实用的工具,可用于在图形界面上进行多边形和矩形的标注,并具备编辑和删除功能。通过这种方式,用户可以更加直观地进行图形标注和处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值