• 题解 Luogu P3187 [HNOI2007]最小矩形覆盖


    题意

    给定平面上 \(n\) 个点,求一个面积最小的矩形覆盖 \(n\) 个点。矩形当然可以不平行于坐标轴。

    保留五位小数。

    \(3 \leq n \leq 5 \times 10^4,x_i,y_i\) 不超过 double 范围,读入有多位小数

    题解

    直觉告诉我们,矩形一定有一条边和凸包上某条边共线。维护以这条边(记为 \(AB\))为底时最左,最右,最上方的点(分别记为 \(L,R,U\))即可。它们在旋转卡壳的过程中一定逆时针旋转。

    关于如何计算面积:

    • 宽很好计算,就是 \(\dfrac{\vec{AB} \times \vec{AU}}{|\vec{AB}|}\)
    • 长可以分别计算 \(\vec{LA}\)\(\vec{BR}\)\(\vec{AB}\) 方向上的投影长度,再和 \(\vec{AB}\) 长度相加。

    如何计算顶点坐标:

    • 顶点 \(P_1\) 为过 \(L\) 到直线 \(AB\) 的垂足
    • 顶点 \(P_2\) 为过 \(R\) 到直线 \(AB\) 的垂足
    • \(U\) 共线的两个顶点可以采用以下方法计算(以左上方为例):计算 \(\vec{UL}\)\(\vec{AB}\) 方向上的投影长度 \(l\),然后 \(U+ \dfrac{l}{|\vec{AB}|} \vec{AB}\) 即可算出该顶点。

    实现时 eps 要设置为 \(10^{-10}\),然后注意判断输出 -0.00 的情况。另外点是否旋转的条件也要做小修改,具体见代码。

    # include <bits/stdc++.h>
    # define Vector Point
    # define DB double
    # define CP const Point
    # define CV const Vector
    const int N=100010,INF=0x3f3f3f3f;
    const DB eps=1e-10;
    const DB Pi=acos(-1);
    struct Point{
    	DB x,y;
    	Point(DB X=0,DB Y=0){
    		x=X,y=Y;
    		return;
    	}
    };
    typedef std::vector <Point> Poly;
    inline int read(void){
    	int res,f=1;
    	char c;
    	while((c=getchar())<'0'||c>'9')
    		if(c=='-')f=-1;
    	res=c-48;
    	while((c=getchar())>='0'&&c<='9')
    		res=res*10+c-48;
    	return res*f;
    }
    inline int Sign(DB x){
    	return (x<-eps)?-1:((x>eps)?1:0);
    }
    inline bool x_comp(CP &a,CP &b){
    	return (Sign(a.x-b.x))?(a.x<b.x):(a.y<b.y);
    }
    inline DB Fabs(DB x){
    	return x*Sign(x);
    }
    inline DB Dot(CV &u,CV &v){
    	return u.x*v.x+u.y*v.y;
    }
    inline DB Cro(CV &u,CV &v){
    	return u.x*v.y-u.y*v.x;
    }
    inline DB Len(CV &u){
    	return sqrt(Dot(u,u));
    }
    Vector operator - (CV &u,CV &v){
    	return Vector(u.x-v.x,u.y-v.y);
    }
    Vector operator + (CV &u,CV &v){
    	return Vector(u.x+v.x,u.y+v.y);
    }
    Vector operator * (CV &u,DB k){
    	return Vector(u.x*k,u.y*k);
    }
    bool operator == (CP &u,CP &v){
    	return !(Sign(u.x-v.x)||Sign(u.y-v.y)); 
    } 
    inline Point PointTurn(CP &u,DB theta){
    	return Point(u.x*cos(theta)+u.y*sin(theta),-u.x*sin(theta)+u.y*cos(theta));
    }
    inline Point PointTurn(CP &u,DB theta,CP &c){
    	return Point((u.x-c.x)*cos(theta)+(u.y-c.y)*sin(theta)+c.x,-(u.x-c.x)*sin(theta)+(u.y-c.y)*cos(theta)+c.y);
    }
    inline bool PointonSeg(CP &u,CP &a,CP &b){
    	return !Sign(Cro(u-a,u-b))&&(Dot(u-a,u-b)<=0);
    }
    inline bool DistoSeg(CP &u,CP &a,CP &b){
    	if(a==b)
    		return Len(u-a);
    	Vector au=u-a,bu=u-b,ab=b-a;
    	if(Sign(Dot(au,ab))<0)
    		return Len(au);
    	if(Sign(Dot(bu,ab))>0)
    		return Len(bu);
    	return Fabs(Cro(au,ab)/Len(ab));
    }
    inline bool PointonLine(CP &u,CP &a,CP &b){
    	return !Sign(Cro(u-a,u-b));
    }
    inline Point FootPoint(CP &u,CP &a,CP &b){
    	Vector au=u-a,bu=u-b,ab=b-a;
    	DB aulen=Dot(au,ab)/Len(ab),bulen=-1.0*Dot(bu,ab)/Len(ab);
    	return a+ab*(aulen/(aulen+bulen));
    }
    inline Point SymPoint(CP &u,CP &a,CP &b){
    	return u+(FootPoint(u,a,b)-u)*2;
    }
    inline Point CrossLL(CP &a,CP &b,CP &c,CP &d){
    	Vector ab=b-a,cd=d-c,ca=a-c;
    	return a+ab*(Cro(cd,ca)/Cro(ab,cd));
    }
    inline bool CrossLS(CP &a,CP &b,CP &c,CP &d){
    	return (PointonLine(CrossLL(a,b,c,d),c,d));
    }
    inline bool CrossSS(CP &a,CP &b,CP &c,CP &d){
    	DB ab_c=Cro(b-a,c-a),ab_d=Cro(b-a,d-a);
    	DB cd_a=Cro(d-c,a-c),cd_b=Cro(d-c,b-c);
    	return (Sign(ab_c)*Sign(ab_d)<0)&&(Sign(cd_a)*Sign(cd_b)<0);
    }
    inline DB PolyArea(Poly &P){
    	DB res=0;
    	for(int i=0;i<(int)P.size();++i){
    		res+=Cro(P[i],P[(i+1)%P.size()]);
    	}
    	return res/2.0;
    }
    inline void ConvexHull(Poly &P,Poly &ans){
    	int n=P.size();
    	std::sort(P.begin(),P.end(),x_comp);
    	ans.resize(0);
    	int siz=0;
    	for(int i=0;i<n;++i){
    		while(siz>1&&Sign(Cro(ans[siz-1]-ans[siz-2],P[i]-ans[siz-2]))<=0)
    			--siz,ans.pop_back();
    		ans.push_back(P[i]),++siz;
    	}
    	for(int i=n-1,st=siz;i>=0;--i){
    		while(siz>st&&Sign(Cro(ans[siz-1]-ans[siz-2],P[i]-ans[siz-2]))<=0)
    			--siz,ans.pop_back();
    		ans.push_back(P[i]),++siz;
    	}
    	ans.pop_back();
    	return;
    }
    const DB epsb=1e-5;
    inline void Solve(Poly &P){
    	int n=P.size();
    	int UP=2,R=1,L=1;
    	DB res=1e18;
    	P.push_back(P[0]);
    	Poly resP;
    	resP.resize(4);
    	for(int i=0;i<n;++i){
    		while(Sign(Cro(P[i+1]-P[i],P[UP]-P[i])-Cro(P[i+1]-P[i],P[UP+1]-P[i]))<=0)
    			UP=(UP+1)%n;
    		while(Sign(Dot(P[i+1]-P[i],P[R]-P[i])-Dot(P[i+1]-P[i],P[R+1]-P[i]))<=0)
    			R=(R+1)%n;
    		if(!i)
    			L=R;
    		while(Sign(Dot(P[i+1]-P[i],P[L]-P[i])-Dot(P[i+1]-P[i],P[L+1]-P[i]))>=0) // 都得加上等号,不要严格大于小于,反正能转就转,保证 L,R 距离最远
    			L=(L+1)%n;
    		DB a=Cro(P[i+1]-P[i],P[UP]-P[i])/Len(P[i+1]-P[i]);
    		DB b=Dot(P[i]-P[L],P[i+1]-P[i])/Len(P[i+1]-P[i])+Len(P[i+1]-P[i])
    		+Dot(P[R]-P[i+1],P[i+1]-P[i])/Len(P[i+1]-P[i]);
    		if(Sign(a*b-res)<0){
    			resP[0]=FootPoint(P[L],P[i],P[i+1]);
    			resP[1]=FootPoint(P[R],P[i],P[i+1]);
    			resP[2]=P[UP]+(P[i+1]-P[i])*(Dot(P[R]-P[UP],P[i+1]-P[i])/Len(P[i+1]-P[i])/Len(P[i+1]-P[i]));
    			resP[3]=P[UP]+(P[i+1]-P[i])*(Dot(P[L]-P[UP],P[i+1]-P[i])/Len(P[i+1]-P[i])/Len(P[i+1]-P[i]));
    			res=a*b;
    		}
    	}
    	printf("%.5lf\n",((Fabs(res)>epsb)?res:0.00000));
    	int idx=0;
    	for(int i=1;i<4;++i){
    		if(Sign(resP[idx].y-resP[i].y)>0||(!Sign(resP[idx].y-resP[i].y)&&Sign(resP[idx].x-resP[i].x)>0)){
    			idx=i;
    		}
    	}
    	for(int i=idx;i<idx+4;++i){
    		double va=resP[i%4].x,vb=resP[i%4].y;
    		if(fabs(va)<epsb)
    			va=0;
    		if(fabs(vb)<epsb)
    			vb=0;
    		printf("%.5lf %.5lf\n",va,vb);
    	}
    	return;
    }
    int n;
    Poly Po,Hull;
    int main(void){
    	n=read();
    	Po.resize(n);
    	for(int i=0;i<n;++i){
    		scanf("%lf%lf",&Po[i].x,&Po[i].y);
    	}
    	ConvexHull(Po,Hull);
    	Solve(Hull);
    	return 0;
    }
    
  • 相关阅读:
    对SpringIOC、AOP的理解
    Java后台与VUE跨域交接
    贼简单的Shiro框架之粗粒度控制菜单栏
    Json
    Spring MVC小DEMO
    面试问题
    多线程理解
    了解java语言
    单点登录如何设计
    进程的创建和调度分析
  • 原文地址:https://www.cnblogs.com/liuzongxin/p/16050745.html
Copyright © 2020-2023  润新知