Vijos[1055]奶牛浴场 极大化思想

题目链接https://vijos.org/p/1055

描述

由于John建造了牛场围栏,激起了奶牛的愤怒,奶牛的产奶量急剧减少。为了讨好奶牛,John决定在牛场中建造一个大型浴场。但是John的奶牛有一个奇怪的习惯,每头奶牛都必须在牛场中的一个固定的位置产奶,而奶牛显然不能在浴场中产奶,于是,John希望所建造的浴场不覆盖这些产奶点。这回,他又要求助于Clevow了。你还能帮助Clevow吗?
John的牛场和规划的浴场都是矩形。浴场要完全位于牛场之内,并且浴场的轮廓要与牛场的轮廓平行或者重合。浴场不能覆盖任何产奶点,但是产奶点可以位于浴场的轮廓上。
Clevow当然希望浴场的面积尽可能大了,所以你的任务就是帮她计算浴场的最大面积。
格式

输入格式

输入文件的第一行包含两个整数L和W,分别表示牛场的长和宽。文件的第二行包含一个整数n,表示产奶点的数量。以下n行每行包含两个整数x和y,表示一个产奶点的坐标。所有产奶点都位于牛场内,即:0<=x<=L,0<=y<=W。
输出格式

输出文件仅一行,包含一个整数S,表示浴场的最大面积。
样例输入1

10 10
4
1 1
9 1
1 9
9 9
样例输出1
80

提示
0<=n<=5000
1<=L,W<=30000
来源
Winter Camp 2002

见王知昆《浅谈用极大化思想解决最大子矩形问题》
这题一眼看去和玉蟾宫这题差不多,但一看数据范围就爆炸了,不能用简单的悬线法来实现
我们可以用第一种算法,枚举所有关键点,不断扫描更新 ans ,具体细节论文中已给出,不做赘述
时间复杂度 O(n2)
代码如下:

#include<algorithm>
#include<ctype.h>
#include<cstdio>
#define N 5020
using namespace std;
inline int read(){
    int x=0,f=1;
    char c=getchar();
    while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
    while(isdigit(c)){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    return x*f;
}
struct Point{
    int x,y;
    Point(int _=0,int __=0):x(_),y(__){}
}a[N];
int n,m,k,ans,up,down;
inline bool cmpp(Point a,Point b){return a.y<b.y;}
inline bool cmp(Point a,Point b){return a.x==b.x?a.y<b.y:a.x<b.x;}
inline void getans(){
    sort(a+1,a+k+1,cmpp);//先按纵坐标排序
    for(int i=1;i<k;i++) ans=max(ans,(a[i+1].y-a[i].y)*m);
}//处理两端在左右边界的举行
inline void getans1(){
    sort(a+1,a+k+1,cmp);//按横坐标排序
    for(int i=3;i<k;i++){//不必扫前两个点(想一想为什么)
        up=0;down=n;
        for(int j=i+1;j<=k;j++){
            ans=max(ans,(down-up)*(a[j].x-a[i].x));//先更新一下答案
            if(a[i].y==a[j].y){//考虑几种特殊的情况
                if(a[j].y-up>down-a[j].y) down=a[j].y;
                else up=a[j].y;
            }
            if(a[j].y>a[i].y)down=min(down,a[j].y);
            if(a[j].y<a[i].y)up=max(up,a[j].y);
        }
    }
}
inline void getans2(){
    for(int i=1;i<=k;i++)
      a[i].x=m-a[i].x;//翻转左右区间,再做一遍
    getans1();
}
int main(){
    n=read();m=read();k=read();
    for(int i=1;i<=k;i++) a[i].x=read(),a[i].y=read();
    a[++k]=Point(0,0);a[++k]=Point(n,0);a[++k]=Point(n,m);a[++k]=Point(0,m);//将四个顶点当做关键点
    getans();getans1();getans2();
    printf("%d",ans);
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值