UVA 12307 Smallest Enclosing Rectangle(旋转卡壳)

题意:给你一些点,找出两个可以包含所有点的矩形,一个保证矩形面积最小,一个保证矩形周长最小,输出两个最小值

题解:首先根据所有点求一个凸包,再在这个凸包上枚举每条边,作为矩形的一条边(这样可以保证最小)

   接着根据旋转卡壳的思想求出另外三条边,这样枚举判断就好

   求另三条边时首先方向是确定了的,找点就是旋转卡壳,思想就是:枚举的任意两条边a与b,a的另三条边与b的另三条边都不会再a与b之间,并且b对应边一定最a对应边的        后面(注意是循环的边)那么就是说,我们可以使用类似双指针方式维护,但是时间复杂度却为O(n)

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define eps 1E-8
/*注意可能会有输出-0.000*/
#define sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型
#define cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化
#define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0
#define mul(a,b) (a<<b)
#define dir(a,b) (a>>b)
typedef long long ll;
typedef unsigned long long ull;
const int Inf=1<<28;
const ll INF=1LL<<60;
const double Pi=acos(-1.0);
const int Mod=1e9+7;
const int Max=500010;
struct Point
{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y) {};
    inline Point operator-(const Point& a)const
    {
        return Point(x-a.x,y-a.y);
    }
    inline bool operator<(const Point& a)const
    {
        return sgn(x-a.x)<0||zero(x-a.x)&&sgn(y-a.y)<0;
    }
        inline Point operator+(const Point& a)const
    {
        return Point(x+a.x,y+a.y);
    }
    inline bool operator!=(const Point& a)const
    {
        return !(zero(x-a.x)&&zero(y-a.y));
    }
};
typedef Point Vector;
struct Line
{
    Point p;
    Vector v;
    double ang;//极角
    Line() {};
    Line(Point p,Vector v):p(p),v(v)
    {
        ang=atan2(v.y,v.x);
    }
    inline bool operator<(const Line& L)const
    {
        return ang<L.ang;
    }
};
double Dis(Point A,Point B)
{
    return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}
double Cross(Vector A,Vector B)
{
    return A.x*B.y-A.y*B.x;
}
int ConvexHull(Point *p,int n,Point *convex)//求凸包
{
    sort(p,p+n);
    int m=0;
    for(int i=0; i<n; ++i)
    {
        while(m>1&&Cross(convex[m-1]-convex[m-2],p[i]-convex[m-2])<0)
        {
            m--;
        }
        convex[m++]=p[i];
    }
    int k=m;
    for(int i=n-2; i>=0; --i)
    {
        while(m>1&&Cross(convex[m-1]-convex[m-2],p[i]-convex[m-2])<0)
        {
            m--;
        }
        convex[m++]=p[i];
    }
    if(n>1)
        m--;
    return m;
}
Point intersection(Point p1,Point p2,Point l1,Point l2)//交点坐标
{
    Point ret=p1;//首先计算直线是否平行
    double t=((p1.x-l1.x)*(l1.y-l2.y)-(p1.y-l1.y)*(l1.x-l2.x))
             /((p1.x-p2.x)*(l1.y-l2.y)-(p1.y-p2.y)*(l1.x-l2.x));
    ret.x+=(p2.x-p1.x)*t;
    ret.y+=(p2.y-p1.y)*t;
    return ret;//线段交点另外判断线段相交(同时判断是否平行)
}
Point now[Max],convex[Max];
double area,per;
double GetArea(Line up,Line down,Line left,Line right)//根据矩形四条线求面积
{
    Point minx=intersection(left.p,left.p+left.v,down.p,down.p+down.v);
    Point miny=intersection(right.p,right.p+right.v,down.p,down.p+down.v);
    Point manx=intersection(left.p,left.p+left.v,up.p,up.p+up.v);
    return Dis(minx,manx)*Dis(minx,miny);
}
double GetPer(Line up,Line down,Line left,Line right)
{
    Point minx=intersection(left.p,left.p+left.v,down.p,down.p+down.v);
    Point miny=intersection(right.p,right.p+right.v,down.p,down.p+down.v);
    Point manx=intersection(left.p,left.p+left.v,up.p,up.p+up.v);
    return (Dis(minx,manx)+Dis(minx,miny))*2;
}
Vector Rotate(Vector A,double rad) //向量A逆时针旋转rad
{
return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad));
}
void RotateStuck(int n)//旋转卡壳枚举矩形
{
    for(int i=0;i<n;++i)
    {
        convex[i+n]=convex[i];
        convex[i+n+n]=convex[i];
    }
    area=per=Inf;
    Line up,down,left,right;//四条直线
    int i=0,j=0,k=0,l=0;//四条线的位置
    for(; i<n; ++i)//枚举上这条线,则可以确定其他
    {
        up=Line(convex[i],convex[i+1]-convex[i]);//其他三条直线所在的点与上这条线成单峰函数
        k=max(i,k);//每次是逆时针旋转,保证是凸包上一条线或者后面的线
        while(Cross(Rotate(up.v,Pi/2),convex[k+1]-convex[k])<0)//通过旋转来判断
            k++;
            left=Line(convex[k],Rotate(up.v,Pi/2));
        j=max(k,j);
        while(Cross(Rotate(up.v,Pi),convex[j+1]-convex[j])<0)
            j++;
            down=Line(convex[j],Rotate(up.v,Pi));
        l=max(j,l);
        while(Cross(Rotate(up.v,3*Pi/2),convex[l+1]-convex[l])<0)
            l++;
            right=Line(convex[l],Rotate(up.v,3*Pi/2));
        area=min(area,GetArea(up,down,left,right));
        per=min(per,GetPer(up,down,left,right));
    }
    return ;
}
int main()
{
    int n;
    while(~scanf("%d",&n)&&n)
    {
        for(int i=0; i<n; ++i)
        {
            scanf("%lf %lf",&now[i].x,&now[i].y);
        }
        int m= ConvexHull(now,n,convex);
        RotateStuck(m);
        printf("%.2f %.2f
",area,per);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/zhuanzhuruyi/p/6371875.html