洛谷 [P1578] WC2002 奶牛浴场

本题是一道用极大化思想求最大子矩阵的经典题目。这个题目很出名,可以在百度搜索王知昆国家队dalao的论文,其中说的非常详细。
先枚举极大子矩形的左边界,然后从左到右依次扫描每一个障碍点,并不断修改可行的上下边界,从而枚举出所有以这个定点为左边界的极大子矩形。
需要注意的是,如果扫描到的点不在当前的上下边界内,那么就不需要对这个点进行处理。

这样做是否将所有的极大子矩形都枚举过了呢?
可以发现,这样做只考虑到了左边界覆盖一个点的矩形,因此我们还需要枚举左边界与整个矩形的左边界重合的情况。这还可以分为两类情况。一种是左边界与整个举行的左边界重合,而右边界覆盖了一个障碍点的情况,对于这种情况,可以用类似的方法从右到左扫描每一个点作为右边界的情况.
hack data:10 10
3 3 0 8 2 3 9
正确答案应该是72。

另一种是左右边界均与整个矩形的左右边界重合的情况,对于这类情况我们可以在预处理中完成:先将所有点按纵坐标排序,然后可以得到以相邻两个点的纵坐标为上下边界,左右边界与整个矩形的左右边界重合的矩形,显然这样的矩形也是极大子矩形,因此也需要被枚举到。
对于开始预处理,需要人为添加0,0;0,l;w,0;l,w四个点

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cmath>
using namespace std;
struct point{
    int x,y;
}num[5005];
int init(){
    int rv=0,fh=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') fh=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        rv=(rv<<1)+(rv<<3)+c-'0';
        c=getchar();
    }
    return fh*rv;
}
int l,w,n,ans,upp,doo;
bool cmp(point a,point b){
    return a.x==b.x?a.y<b.y:a.x<b.x;
}
bool cmp2(point a,point b){
    return a.y<b.y;
}
int main(){
    freopen("in.txt","r",stdin);
    l=init();w=init();n=init();
    for(int i=1;i<=n;i++){
        num[i].x=init();num[i].y=init();
    }
    num[n+1].x=0;num[n+1].y=0;
    num[n+2].x=0;num[n+2].y=w;
    num[n+3].x=l;num[n+3].y=w;
    num[n+4].x=l;num[n+4].y=0;
    n+=4;
    sort(num+1,num+n+1,cmp);
    for(int i=1;i<=n;i++){
        upp=0,doo=w;
        int v=l-num[i].x;
        for(int j=i+1;j<=n;j++){
            if(num[j].y>=upp&&num[j].y<=doo){
                if(v*(doo-upp)<=ans) break;
                ans=max(ans,(doo-upp)*(num[j].x-num[i].x));
                if(num[i].y==num[j].y) break;
                if(num[i].y<num[j].y) doo=min(doo,num[j].y);
                else upp=max(upp,num[j].y);
            }
        }
        upp=0;doo=w;v=num[i].x;
        for(int j=i-1;j>=1;j--){
            if(num[j].y>=upp&&num[j].y<=doo){
                if((doo-upp)*v<=ans) break;
                ans=max(ans,(doo-upp)*(num[i].x-num[j].x));
                if(num[i].y==num[j].y) break;
                if(num[j].y>num[i].y) doo=min(doo,num[j].y);
                else upp=max(upp,num[j].y);
            }
        }
    }
    sort(num+1,num+1+n,cmp2);
    for(int i=1;i<=n-1;i++){
        ans=max(ans,(num[i+1].y-num[i].y)*l);
    }
    cout<<ans;
    fclose(stdin);
    return 0;
}

转载于:https://www.cnblogs.com/Mr-WolframsMgcBox/p/7911846.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值