[HNOI2007]最小矩形覆盖(凸包+旋转卡壳)

题目描述
给定一些点的坐标,要求求能够覆盖所有点的最小面积的矩形,输出所求矩形的面积和四个顶点坐标

预备知识
凸包:https://blog.csdn.net/qq_30974369/article/details/76405546
旋转卡壳:https://www.cnblogs.com/adelalove/p/8467472.html
图1:
[HNOI2007]最小矩形覆盖(凸包+旋转卡壳)_第1张图片
图2:
[HNOI2007]最小矩形覆盖(凸包+旋转卡壳)_第2张图片
题解:
首先求出凸包,矩形的一条边一定在和凸包的一条边重合。然后旋转卡壳维护最上点。最左点和最右点也可以用旋转卡壳维护,或者用三分维护。在卡壳时根据图1片用点积求l2的长度,由于最左点最右点的角度在90~180度之间,并且点积=|a||b|cosx,此时cosx小于0,所以点积越小,说明l2越长。
接下来求矩阵面积,矩形的高很容易求,由三个点s[i],s[i+1],s[a]的叉积求出三角形面积的2倍,除以底边长度len(s[i],s[i+1]),即为三角形的高,矩形的宽。
然后求矩形的长,如图2,矩形为EFGH,矩形的长为|EF|,|EF|=|Es[i]|+|Fs[i+1]|-|s[i]s[i+1]|,这里|s[i]s[i+1]|很好求,所以我们要求|Es[i]|,|Fs[i+1]|,我们求出向量|s[b]s[i]|和|s[i+1]s[i]|的点积,|s[b]s[i]| |s[i+1]s[i]|cosx,注意|s[b]s[i]|就是直角三角形s[b]Es[i]的斜边,所以Es[i]=|s[b]s[i]|cosx,Es[i]=|s[b]s[i]| |s[i+1]s[i]|cosx/|s[i+1]s[i]|,即为点积/|s[i+1]s[i]|。求|Fs[i+1]|同理。
接下来求四个点,如图2,求F点即为点s[i+1]+向量|s[i+1]s[i]|*(len(Fs[i+1])/len(|s[i+1]s[i]|))即可。其它三个点也可以通过求出来的点逆时针推过去。
最后,由于坐标可能出现-0的情况,我们需要把-0转成0。

#include
using namespace std;
const int N=1e5+5;
const double eps=1e-8;
int n,top;
double ox,oy;
struct node
{
    double x,y;
    node(double x=0,double y=0):x(x),y(y){}
    bool operator<(const node&o)const
    {
        double a=atan2(y-oy,x-ox),b=atan2(o.y-oy,o.x-ox);
        if(fabs(a-b)=2&&cross(s[top]-s[top-1],a[i]-s[top-1])<=0) top--;
        s[++top]=a[i];
    }
}
void solve2()
{
    double ans=1e100;
    for(int i=1;i<=top;i++) s[i+top]=s[i];
    int a,b,c;a=b=c=2;
    for(int i=1;i<=top+1;i++)
    {
        while(cross(s[a]-s[i+1],s[i]-s[i+1])<=cross(s[a+1]-s[i+1],s[i]-s[i+1])) a++;
        while(dot(s[b]-s[i+1],s[i]-s[i+1])>=dot(s[b+1]-s[i+1],s[i]-s[i+1])) b++;
        if(i==1) c=a;
        while(dot(s[c]-s[i],s[i+1]-s[i])>=dot(s[c+1]-s[i],s[i+1]-s[i])) c++;
        double dis=len(s[i]-s[i+1]);
        double l=dot(s[b]-s[i],s[i+1]-s[i])/dis;
        double r=dot(s[c]-s[i+1],s[i]-s[i+1])/dis;
        double h=cross(s[i]-s[a],s[i+1]-s[a])/dis;
        l=fabs(l);r=fabs(r);h=fabs(h);
        double t=(l+r-dis)*h;
        if(t

你可能感兴趣的:([HNOI2007]最小矩形覆盖(凸包+旋转卡壳))