洛谷3187 BZOJ1185 最小矩形覆盖 旋转卡壳 凸包

题目链接

题意:给你n个点,让你找到一个面积最小的矩形覆盖住所有的点,输出最小矩形面积和矩形四个顶点的坐标。

题解:
首先把所有点覆盖其实只需要把凸包上的所有点覆盖就行了,因为其他点已经被凸包上的点覆盖了。然后我看了别人的题解,似乎是可以证明,最优的情况矩形一定会有一条边与凸包上的一条边重合。当然也可以通过大胆猜想与直观感知来得到这一结论。

有了这个结论,我们就考虑枚举凸包上的边,我们确定了一条边之后,我们为了方便描述,看作把凸包旋转成这条边位于凸包下侧的情况。那么我们相当于找到了矩形下面的那条边,需要找的是左侧、右侧和上侧的边。左侧的边肯定要覆盖最靠左的点,右侧要覆盖最靠右的点,上侧要覆盖最靠上的点,那么我们要先找到这三个点。由于点积的几何意义是一个向量在另一个向量上的投影,所以我们只需要找点积最小和点积最大的两个点,最小的就是最左侧的,最大的就是最右侧的。至于最上分的,直接用叉积找,叉积最大的是最远的,因为相当于是一个底固定,高不固定的平行四边形面积,显然面积越大高越大,找到的点就离点越远。找到其他三个点之后,我们可以通过一些向量的运算来算出矩形的四个顶点的坐标和矩形的面积,具体可以看代码,都不难理解。

但是每次都这样做一遍复杂度不对啊,然后我们发现,枚举下一条边时左侧、右侧、上侧最远点都是单调的,于是可以旋转卡壳,这样最终复杂度就是 O(nlogn) O ( n l o g n ) 了,复杂度瓶颈在于给点排序,其他操作应该都是 O(n) O ( n ) 的。

代码:

#include <bits/stdc++.h>
using namespace std;

int n,tp,ji;
double eps=1e-12,ans=2e9,d,ld,rd,w,h;
struct Point
{
    double x,y;
}a[500010],sta[500010],res[10];
struct Vector
{
    double x,y;
};
int cmp(Point x,Point y)
{
    if(x.y==y.y)
    return x.x<y.x;
    return x.y<y.y;
}
double chaji(Vector x,Vector y)
{
    return x.x*y.y-x.y*y.x;
}
double dianji(Vector x,Vector y)
{
    return x.x*y.x+x.y*y.y;
}
double dis(Point x,Point y)
{
    return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));
}
int dcmp(double x)
{
    if(fabs(x)<eps)
    return 0;
    if(x>0)
    return 1;
    return -1;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    scanf("%lf%lf",&a[i].x,&a[i].y);
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;++i)
    {
        Vector x,y;
        while(tp>1)
        {
            x.x=sta[tp].x-sta[tp-1].x;
            x.y=sta[tp].y-sta[tp-1].y;
            y.x=a[i].x-sta[tp-1].x;
            y.y=a[i].y-sta[tp-1].y;
            if(dcmp(chaji(x,y))<=0)
            --tp;
            else
            break;
        }
        sta[++tp]=a[i];
    }
    ji=tp;
    for(int i=n-1;i>=1;--i)
    {
        Vector x,y;
        while(tp>ji)
        {
            x.x=sta[tp].x-sta[tp-1].x;
            x.y=sta[tp].y-sta[tp-1].y;
            y.x=a[i].x-sta[tp-1].x;
            y.y=a[i].y-sta[tp-1].y;
            if(dcmp(chaji(x,y))<=0)
            --tp;
            else
            break;
        }
        sta[++tp]=a[i];
    }
    if(n>1)
    --tp;
    int l=2,r=2,t=2;
    for(int i=1;i<=tp;++i)
    {
        d=dis(sta[i],sta[i+1]);
        Vector x,y,z;
        while(1)
        {
            x.x=sta[i+1].x-sta[i].x;
            x.y=sta[i+1].y-sta[i].y;
            y.x=sta[t+1].x-sta[i].x;
            y.y=sta[t+1].y-sta[i].y;
            z.x=sta[t].x-sta[i].x;
            z.y=sta[t].y-sta[i].y;
            if(dcmp(chaji(x,y)-chaji(x,z))>=0)
            t=t%tp+1;
            else
            break;
        }
        if(i==1)
        r=t;
        while(1)
        {
            x.x=sta[i+1].x-sta[i].x;
            x.y=sta[i+1].y-sta[i].y;
            y.x=sta[l+1].x-sta[i].x;
            y.y=sta[l+1].y-sta[i].y;
            z.x=sta[l].x-sta[i].x;
            z.y=sta[l].y-sta[i].y;
            if(dcmp(dianji(x,y)-dianji(x,z))>=0)
            l=l%tp+1;
            else
            break;
        }
        while(1)
        {
            x.x=sta[i+1].x-sta[i].x;
            x.y=sta[i+1].y-sta[i].y;
            y.x=sta[r+1].x-sta[i].x;
            y.y=sta[r+1].y-sta[i].y;
            z.x=sta[r].x-sta[i].x;
            z.y=sta[r].y-sta[i].y;
            if(dcmp(dianji(x,y)-dianji(x,z))<=0)
            r=r%tp+1;
            else
            break;
        }
        x.x=sta[i+1].x-sta[i].x;
        x.y=sta[i+1].y-sta[i].y;
        y.x=sta[l].x-sta[i].x;
        y.y=sta[l].y-sta[i].y;
        ld=fabs(dianji(x,y)/d);
        y.x=sta[r].x-sta[i].x;
        y.y=sta[r].y-sta[i].y;
        rd=fabs(dianji(x,y)/d);
        w=ld+rd;
        y.x=sta[t].x-sta[i].x;
        y.y=sta[t].y-sta[i].y;
        h=fabs(chaji(x,y))/d;
        if(w*h<ans)
        {
            ans=w*h;
            res[1].x=sta[i].x+(sta[i+1].x-sta[i].x)*(ld/d);
            res[1].y=sta[i].y+(sta[i+1].y-sta[i].y)*(ld/d);
            res[2].x=res[1].x+(sta[l].x-res[1].x)*(h/dis(sta[l],res[1]));
            res[2].y=res[1].y+(sta[l].y-res[1].y)*(h/dis(sta[l],res[1]));
            res[3].x=res[2].x+(sta[t].x-res[2].x)*(w/dis(res[2],sta[t]));
            res[3].y=res[2].y+(sta[t].y-res[2].y)*(w/dis(res[2],sta[t]));
            res[4].x=res[3].x+(sta[r].x-res[3].x)*(h/dis(sta[r],res[3]));
            res[4].y=res[3].y+(sta[r].y-res[3].y)*(h/dis(sta[r],res[3]));
        }
    }
    printf("%.5lf\n",ans);
    int ji=1;
    for(int i=2;i<=4;++i)
    {
        if(res[i].y<res[ji].y)
        ji=i;
        else if(res[i].y==res[ji].y&&res[i].x<res[ji].x)
        ji=i;
    }
    for(int i=5;i<=8;++i)
    res[i]=res[i-4];
    for(int i=0;i<=3;++i)
    {
        if(res[ji+i].x<eps)
        res[ji+i].x=0.0;
        if(res[ji+i].y<eps)
        res[ji+i].y=0.0;
        printf("%.5lf %.5lf\n",res[ji+i].x,res[ji+i].y);
    }   
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值