[Vijos1055]奶牛浴场(极大子矩形)

来源:互联网 发布:编程可以做什么 编辑:程序博客网 时间:2024/05/13 09:59

=== ===

这里放传送门

=== ===

题解

这道题是最大子矩形的模板题啦。就是说在一个矩形里面给出一些障碍点的坐标,求不包含障碍点并且边框与矩形边界平行的最大的子矩形。

求这种东西有两种方法,一个是这里用的极大化思想,就是对枚举的一种优化,保证每次枚举的子矩形都是一个极大的子矩形,减少了很多重复的枚举,时间复杂度是O(s2)的,s为障碍点个数;还有一种方法叫做悬线法,本质上是一种DP,时间复杂度是O(nm)的。这两种做法分别适用于障碍点稀疏和密集的情况。

这里使用的极大化思想的基本思路有点类似于贪心的,首先把所有障碍点按照横坐标排序,依次选取每个点i作为矩形的左边界,上下边界随着枚举来维护,初始值就是整个矩形的上下边界。然后枚举在i后面的点j作为矩形的右边界,每次先根据当前维护的上下边界算一下面积来更新答案,然后再根据j的纵坐标来更新上下边界。比如当点j在点i上面的时候为了保证后面枚举到的所有子矩形都不包含点j,所以要把上边界“缩”到点j那个位置。但是还要加一个特判就是当点j本来就在当前枚举的上边界上面的时候就不需要更新了。然后为了枚举的时候能够更新到矩形边界的位置,在矩形的四个角位置增加了四个障碍点。

但是这样处理的时候有两类遗漏的情况,第一种就是矩形的左边界和极大子矩形的左边界重合的时候没计算到,因为枚举障碍点的时候并没有枚举左边新添加的那两个障碍点。一开始(愚蠢地)认为把那两个点枚举过去就能解决问题,但是可能有一些长这个样子的它就枚举不到。
这里写图片描述
如果把左边界上的点全都搞成障碍点当然更离谱了。。时间复杂度要炸飞了好吧= =但其实把矩形左右颠倒再做一遍就能解决问题啦。第二类遗漏的情况就是左右边界都和矩形重合的部分,这个也很好解决,这种情况肯定是那个矩形卡在相邻两个障碍点之间的,预处理一下就可以了。

代码

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;struct point{    int x,y;    bool operator < (const point &a) const    {return x<a.x;}}p[6010];int l,w,ans,n;int comp(point a,point b){    return a.y<b.y;}void add(int x,int y){    n++;    p[n].x=x;p[n].y=y;}int pre_trt(){    int sum=0;    for (int i=2;i<=n;i++)      sum=max(sum,(p[i].y-p[i-1].y)*w);    return sum;}int Max_sub(){    int sum=0,up,down,left;    for (int i=3;i<n;i++)//从读入的第一个障碍点开始枚举,即不枚举最左边的点    {//因为这些点在处理遗漏情况的时候就会包括进去        up=0;down=l;        for (int j=i+1;j<=n;j++)        {//每枚举一个j,先计算出它对应的结果            sum=max(sum,(down-up)*(p[j].x-p[i].x));            if (p[j].y==p[i].y)              if (p[j].y-up>down-p[j].y) down=p[j].y;              else up=p[j].y;            if (p[j].y<p[i].y) up=max(up,p[j].y);            if (p[j].y>p[i].y) down=min(down,p[j].y);        }    }    return sum;}void upside(){    for (int i=1;i<=n;i++)      p[i].x=w-p[i].x;//把图左右颠倒,点(0,1)变成点(w,1),以此类推    sort(p+1,p+n+1);}int main(){    scanf("%d%d%d",&l,&w,&n);    for (int i=1;i<=n;i++)      scanf("%d%d",&p[i].x,&p[i].y);    add(0,0);add(0,w);add(l,0);add(l,w);    sort(p+1,p+n+1,comp);//预处理之前先按纵坐标排序    ans=pre_trt();//预处理    sort(p+1,p+n+1);//按横坐标排序    ans=max(ans,Max_sub());    upside();//把整个图左右颠倒    ans=max(ans,Max_sub());    printf("%d",ans);    return 0;}
0 0