描述
由于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;
}