省选专练之HNOI2007最小矩形覆盖&模板:旋转卡壳

本文介绍了一种计算平面凸多边形最小面积包围矩形的算法。首先通过Graham扫描法找到凸包,然后枚举矩形的一条边在凸包上的所有可能位置,利用点乘和叉乘计算矩形的宽和高,最终确定最小面积的矩形。代码中详细展示了如何实现这一过程,包括点的定义、比较操作、Graham扫描法、以及最小矩形的计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1:先求出凸包

2:由于矩形的一条边必然在凸包上枚举这条边

3:左右(l,r)利用点乘算出(因为存在cos)

4:上下(p)利用叉乘算出(因为存在sin)

#include<bits/stdc++.h>
using namespace std;
const double eps=1e-12;
const int N=2e6+100;
int cmp(double A){
    if(fabs(A)<eps)return 0;
    return A>0?1:-1;
}
int equal(double A,double B){
    return (cmp(A-B)==0);
}
struct Point{
    double x,y;
    Point(double _x=0.0,double _y=0.0):x(_x),y(_y){}
    friend bool operator <= (Point A,Point B){return fabs(A.y-B.y)<eps?A.x<B.x:A.y<B.y;}
    friend bool operator < (Point A,Point B){return A.x<B.x||(A.x==B.x&&A.y<B.y);}
    friend Point operator + (Point A,Point B) {return Point(A.x+B.x,A.y+B.y);}
    friend Point operator - (Point A,Point B) {return Point(A.x-B.x,A.y-B.y);}
    friend Point operator * (Point A,double k){return Point(A.x*k,A.y*k)    ;}
    friend Point operator / (Point A,double k){return Point(A.x/k,A.y/k)    ;}
    friend bool operator == (Point A,Point B) {return A.x==B.x&&A.y==B.y;}
    friend double dis 		(Point A		 ){return sqrt(A.x*A.x+A.y*A.y) ;} 
}P[N],Q[N],A[5];
int top=0;
int n;
double Cross(Point A,Point B){
    return A.x*B.y-A.y*B.x;
}
double Dot(Point A,Point B){
    return A.x*B.x+A.y*B.y;
}
void Push(Point now){
    while(Cross((now-Q[top-1]),(Q[top]-Q[top-1]))>0||now==Q[top])top--;
    top++;
    Q[top]=now;
}
void Graham(){
    sort(P+1,P+1+n);
    Q[0]=Q[top=1]=P[1];
    for(int i=2;i<=n;i++){
//		if(P[i].x==3.6071)cout<<i<<'\n';
        Push(P[i]);
    }
    for(int i=n-1;i>=2;i--){
        Push(P[i]);
    }
    Q[0]=Q[top];
}
double ans=1e18;
void RC(){
    int l=1;
    int r=1;
    int p=1;
//	for(int i=0;i<top;i++){
//		cout<<Q[i].x<<" "<<Q[i].y<<'\n';
//	}
    for(int i=0;i<top;i++){
//		cout<<Q[i].x<<" "<<Q[i].y<<'\n';
//		cout<<i<<" "<<p<<'\n';
        while(cmp(Cross(Q[i+1]-Q[i],Q[p+1]-Q[i])  -Cross(Q[i+1]-Q[i],Q[p]-Q[i])>=0) ){
            p=(p+1)%top;
//			cout<<"Cut"<<" "<<p<<'\n';

        }		
        while(cmp(Dot  (Q[i+1]-Q[i],Q[r+1]-Q[i])  -Dot  (Q[i+1]-Q[i],Q[r]-Q[i]))>=0 )r=(r+1)%top;
        if(i==0)l=r;
        while(cmp(Dot  (Q[i+1]-Q[i],Q[l+1]-Q[i])-Dot  (Q[i+1]-Q[i],Q[l]-Q[i]))   <=0)l=(l+1)%top;
        double D=dis(Q[i+1]-Q[i]);
        double L=Dot  (Q[i+1]-Q[i],Q[l]-Q[i])/D;
        double R=Dot  (Q[i+1]-Q[i],Q[r]-Q[i])/D;
        double H=Cross(Q[i+1]-Q[i],Q[p]-Q[i])/D;
//		cout<<D<<" "<<H<<" "<<L<<" "<<R<<'\n';
//		cout<<"------"<<'\n';
        H=fabs(H);
        double tmp=fabs(R-L)*H;
//		ans=min(ans,fabs(R-L)*H);
        if(tmp<ans){
            ans=tmp;
            A[0]=Q[i]+(Q[i+1]-Q[i])*(R/D);
            A[1]=A[0]+(Q[r]-A[0])*(H/dis(A[0]-Q[r]));
            A[2]=A[1]-(A[0]-Q[i])*((R-L)/dis(Q[i]-A[0]));
            A[3]=A[2]-(A[1]-A[0]);
        }
    }
}
struct Node{
    double x,y;
}B[5];
bool comp(const Node &A, const Node &B) {
    return (A.y<B.y)||(A.y==B.y&&A.x<B.x);
}
int main(){
// 	freopen("1986.in","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lf%lf",&P[i].x,&P[i].y);
    }
    Graham();
//	cout<<"Graham"<<'\n';
    RC();
    printf("%.5lf\n",ans);
    int fir=0;
    for(int i=1;i<=3;i++)
        if(A[i]<=A[fir])
            fir=i;
    for(int i=0;i<=3;i++)
        printf("%.5lf %.5lf\n",fabs(A[(i+fir)%4].x)>1e-12?A[(i+fir)%4].x:0.00000,fabs(A[(i+fir)%4].y)>1e-12?A[(i+fir)%4].y:0.00000);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值