欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

求散点最小覆盖矩形(凸包最小外接矩形)

程序员文章站 2022-04-02 19:24:43
...

问题描述: 给一堆散点,要求一个最小面积的矩形能覆盖所有的散点。

思路: 先散点建立凸包,然后用旋转卡壳的思想枚举边,枚举到每条边时,再找对应的最左、最右、最上方的一个点,这些点一定在外接矩形上,找到面积最小的矩形即可。

时间复杂度:O(N)

例题:最小矩形覆盖

#include<bits/stdc++.h>
#define ll long long
#define lf double
#define IOS std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Rep(i,l,r) for(int i=(l);i<=(r);i++)
using namespace std;

struct Point {double x, y;};
typedef Point vector_t;
typedef Point point_t;

const double eps = 1e-8;
int sgn(double x){return (x>eps)-(x<-eps);};

double Cross(const vector_t& x, const vector_t& y) {return x.x * y.y - x.y * y.x;}  //叉积
vector_t operator*(const vector_t& v, double t) {return (vector_t){v.x * t, v.y * t};}
vector_t operator+(const vector_t& a, const vector_t& b) {return (vector_t){a.x + b.x, a.y + b.y};}
Point operator-(const Point& a, const Point& b) {return (Point){a.x - b.x, a.y - b.y};}
bool operator <(Point a,Point b){return !sgn(a.y-b.y)?a.x<b.x:a.y<b.y;}

double Dot(vector_t A, vector_t B){
    return A.x*B.x + A.y*B.y;
}

//取模(长度)
double Length(vector_t A){
    return sqrt(Dot(A, A));
}

int build(int n,Point* p,Point* s)
{
	//*p为散点,*s为凸包的点
    sort(p,p+n);
    int m = 0;
    for(int i = 0;i < n;i++)
    {
        while(m>1&&Cross((s[m-1]-s[m-2]),(p[i]-s[m-2]))<=0)m--;
        s[m++] = p[i];
    }
    int lim = m;
    for(int i = n-2;i >= 0;i--)
    {
        while(m>lim&&Cross((s[m-1]-s[m-2]),(p[i]-s[m-2]))<=0)m--;
        s[m++] = p[i];
    }
    if(m!=1)m--;
    //注意范围 s[0,m)
    return m;
}

double NoNegativeZero(double x){return !sgn(x)?fabs(x):x;}//防止输出-0
void GetMaxRec(int m,Point* s)
{
    Point rec[4];//rectangle,矩形的4个点
    int le=1,ri=1,up=1;
    double L,R,H,D,tmp;
    double recArea=1e60;//矩形面积
    for(int i=0;i<m;++i)
    {
        D=Length(s[i]-s[i+1]);
        while(sgn(Cross(s[i+1]-s[i],s[up]-s[i])-Cross(s[i+1]-s[i],s[up+1]-s[i]))<=0)up=(up+1)%m;
        while(sgn(Dot(s[i+1]-s[i],s[ri]-s[i])-Dot(s[i+1]-s[i],s[ri+1]-s[i]))<=0)ri=(ri+1)%m;
        if(i==0)le=up;
        while(sgn(Dot(s[i+1]-s[i],s[le]-s[i])-Dot(s[i+1]-s[i],s[le+1]-s[i]))>=0)le=(le+1)%m;
        L=Dot(s[i+1]-s[i],s[le]-s[i])/D;
        R=Dot(s[i+1]-s[i],s[ri]-s[i])/D;
        H=Cross(s[i+1]-s[i],s[up]-s[i])/D;
        H=H>0?H:-H;
        tmp=(R-L)*H;
        if(tmp<recArea)
        {
            recArea=tmp;
            rec[0]=s[i]+(s[i+1]-s[i])*(R/D);
            rec[1]=rec[0]+(s[ri]-rec[0])*(H/Length(s[ri]-rec[0]));
            rec[2]=rec[1]-(rec[0]-s[i])*((R-L)/Length(s[i]-rec[0]));
            rec[3]=rec[2]-(rec[1]-rec[0]);
        }
    }
    //recArea为最小矩形面积,rec[]为矩形4个点
    printf("%.5lf\n",fabs(recArea));
    int fir=0;//例题特殊处理,需要排序
    for(int i=1;i<=3;++i)if(rec[i]<rec[fir])fir=i;
    for(int i=0;i<=3;++i)
        printf("%.5lf %.5lf\n",NoNegativeZero(rec[(i+fir)%4].x),NoNegativeZero(rec[(i+fir)%4].y));
}

Point p[50005];
int n;
Point s[50005];

int main()
{
    cin>>n;
    Rep(i,0,n-1){
        cin>>p[i].x>>p[i].y;
    }
    int m=build(n,p,s);
    GetMaxRec(m,s);

    return 0;
}