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

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

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

时间复杂度: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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值