【旋转卡壳】最小矩形覆盖

题意:

【旋转卡壳】最小矩形覆盖_第1张图片


分析:

这道题是旋转卡壳的一个典型问题
首先我们瞎蒙一个性质:
我们目标状态的矩形一定有一条边是凸包上的边(的延长线)
其实是可以证明的:
首先,最小矩形必然每条边上都有点
那么如果不是凸包上的边:
【旋转卡壳】最小矩形覆盖_第2张图片
那么我们可以通过旋转这个矩形,使得这个矩形的某条边上不再有点
【旋转卡壳】最小矩形覆盖_第3张图片
所以,我们只需要找出凸包,暴力枚举每条边,用旋转卡壳来确定这个矩形。
那么,如何卡壳呢?
很简单,我们考虑叉积和点积的几何意义:
叉积:可以求出一个点与一条线所构成的三角形面积。
点积:可以求出一条向量在另一条上的投影长度。
那么根据这两点,我们可以通过叉积找到离当前线段最远的点,通过点积找到离当前线段最左/右的点,通过这三个点和这条线段,我们就可以确定这个矩形了。当然,我们需要按照顺时针/逆时针的顺序依次枚举每条边,然后再贪心地确定三个特殊点即可。

#include
#include
#include
#include
#include
#define SF scanf
#define PF printf
#define MAXN 100010
#define EPS 1e-8
using namespace std;
struct node{
    double x,y;
    node(){}
    node(double xx,double yy):x(xx),y(yy) {}
    node operator + (const node &a) const {
        return node(x+a.x,y+a.y);
    }
    node operator - (const node &a) const {
        return node(x-a.x,y-a.y);
    }
    node operator * (const double &t) const {
        return node(x*t,y*t);
    }
    double operator *(const node &a) const{
        return x*a.x+y*a.y;
    } 
    double operator ^(const node &a) const{
        return x*a.y-y*a.x;
    }
    /*bool operator <(const node &a) const {
        return y
    bool operator <(const node &a) const {
        return fabs(y-a.y)5];
stack s1,s;
double ans=1e60;
int n,cnt1;
void solve(node a1[],int &cnt){
    s.push(p[1]);
    s1.push(p[1]);
    s1.push(p[2]);
    for(int i=3;i<=n;i++){
        while(!s.empty()&&((p[i]-s.top())^(s1.top()-s.top()))<=0){
            s.pop();
            s1.pop();
        }
        s.push(s1.top());
        s1.push(p[i]);
    }
    while(!s1.empty()){
        a1[++cnt]=s1.top();
        s1.pop();
    }
    while(!s.empty())
        s.pop();
}
double len(node a){
    return sqrt(a.x*a.x+a.y*a.y);
}
bool cmp(node a,node b){
    return bvoid rc(){
    int l=1,r=1,p=1;
    double L,R,D,H;
    for(int i=0;i1]);
        while((((l1[i+1]-l1[i])^(l1[p+1]-l1[i]))-((l1[i+1]-l1[i])^(l1[p]-l1[i]))) >-EPS)
            p=(p+1)%cnt1;
        while(((l1[i+1]-l1[i])*(l1[r+1]-l1[i]))-((l1[i+1]-l1[i])*(l1[r]-l1[i]))>-EPS)
            r=(r+1)%cnt1;
        if(i==0)
            l=r;
        while(((l1[i+1]-l1[i])*(l1[l+1]-l1[i]))-((l1[i+1]-l1[i])*(l1[l]-l1[i]))1)%cnt1;
        L=(l1[i+1]-l1[i])*(l1[l]-l1[i])/D;
        R=(l1[i+1]-l1[i])*(l1[r]-l1[i])/D;
        H=((l1[i+1]-l1[i])^(l1[p]-l1[i]))/D;
        H=fabs(H);
        double tmp=(R-L)*H;
        if(tmp0]=l1[i]+(l1[i+1]-l1[i])*(R/D);
            t[1]=t[0]+(l1[r]-t[0])*(H/len(t[0]-l1[r]));
            t[2]=t[1]-(t[0]-l1[i])*((R-L)/len(l1[i]-t[0]));
            t[3]=t[2]-(t[1]-t[0]);
        }
    }
}
int main(){
    SF("%d",&n);
    for(int i=1;i<=n;i++)
        SF("%lf%lf",&p[i].x,&p[i].y);
    sort(p+1,p+1+n,cmp);
    solve(l1,cnt1);
    sort(p+1,p+1+n);
    cnt1--;
    solve(l1,cnt1);
    cnt1--;
    l1[0]=l1[cnt1];
    /*for(int i=0;i<=cnt1;i++)
        PF("[%.3lf %.3lf]\n",l1[i].x,l1[i].y);*/
    rc();
    PF("%.5lf\n",ans);
    int fir=0;
    for(int i=1;i<4;i++)
        if(t[i]for(int i=0;i<4;i++)
        PF("%.5lf %.5lf\n",t[(i+fir)%4].x,t[(i+fir)%4].y);
}

你可能感兴趣的:(计算几何,旋转卡壳)