Vijos 1055 奶牛浴场 最大子矩阵 算♂法①

题意:链接

方法:最大子矩阵之算♂法①

解析:

首先谈到最大子矩阵,我们可能会想到之前做过的盖房子?,那道DP求解的题目。

然而这种题目当然有更高♂端的算法。

比如接下来要谈到的算法①。

我们先来观察数据范围,n,m<=30000,这下就玩完了,怎么dp?

一下子就D掉了你原来的算法,真是不留情面。

那么我们来介绍一种新的算法。

首先谈暴力,枚举各种坏点,但这种的复杂度呢?甚至可能达到6次方,所以怎么优化呢?

按照经验,这种坐标图排个序就能降下复杂度什么的。

于是有神犇介绍了s^2复杂度的算法,其中s为坏点数目。

首先呢,我们先将所有的坏点按照x第一关键字,y第二关键字排序,之后呢,我们枚举基准坏点,也就是作为在左边界上的坏点。假设初始的上边界为最高行,下边界为最低行。之后再扫描在其右面的所有坏点。

每扫到一个点,我们就知道二者的横坐标差距乘以上下边界差距就是目前的一个极大子矩阵的面积。

更新完答案后,还需要对上下边界进行更新,对于本题(即坏点可存在于边界处),当坏点在枚举的点的上方(含在同一行)的时候,如果坏点比当前的上边界要低,则需要更新上边界,要保证在该坏点之后枚举到的极大子矩阵不会包含该坏点。

下边界的讨论同理。

在每一次枚举的最后不要忘了更新一次包含边界的情况。

当然,我们发现,如果从右向左枚举的时候,是不会有包含左边界的情况的(某点在左边界除外),所以为了不遗漏情况,我们还需要从右向左枚举一次。

这样就结束了吗?

并没有。

左右边界都包含的情况并没有枚举到。

所以需要特殊处理一下左右边界都包含的情况,这里我采用的是重新按照y排序处理的。

然后就水过了~

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 5010
using namespace std;
int l,w,n;
struct node
{
    int x,y;
}pt[N];
int cmp(node a,node b)
{
    if(a.x==b.x)return a.y<b.y;
    return a.x<b.x;
}
int cmp2(node a,node b)
{
    if(a.y==b.y)return a.x<b.x;
    return a.y>b.y;
} 
int main()
{
    scanf("%d%d%d",&l,&w,&n);
    if(!n){printf("%d\n",l*w);return 0;}
    for(int i=1;i<=n;i++)scanf("%d%d",&pt[i].x,&pt[i].y);
    sort(pt+1,pt+n+1,cmp);
    int ans=-1;
    for(int i=1;i<=n;i++)
    {
        int u=w,d=0;
        for(int j=i+1;j<=n;j++)
        {
            ans=max(ans,(pt[j].x-pt[i].x)*(u-d));
            if(pt[j].y>=pt[i].y)u=min(u,pt[j].y);
            if(pt[j].y<=pt[i].y)d=max(d,pt[j].y); 
        }
        ans=max(ans,(l-pt[i].x)*(u-d));
    }
    for(int i=n;i>=1;i--)
    {
        int u=w,d=0;
        for(int j=i-1;j>=1;j--)
        {
            ans=max(ans,(pt[i].x-pt[j].x)*(u-d));
            if(pt[j].y>=pt[i].y)u=min(u,pt[j].y);
            if(pt[j].y<=pt[i].y)d=max(d,pt[j].y); 
        }
        ans=max(ans,pt[i].x*(u-d)); 
    }
    sort(pt+1,pt+n+1,cmp2);
    for(int i=1;i<n;i++)
    {
        ans=max(ans,(pt[i].y-pt[i+1].y)*l); 
    }
    ans=max(ans,(w-pt[1].y)*l);
    ans=max(ans,pt[n].y*l);
    printf("%d\n",ans); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值