P3187

problem

给定 \(n\) 个点的坐标,求能够覆盖所有点的最小面积的矩形,输出所求矩形的面积和四个顶点坐标。

solution

感性理解可知最小覆盖矩形的一条边肯定与凸包的一条边重合。很明显是先跑出凸包然后旋转卡壳维护每个向量最上、最左、最右的点,并更新答案。

但是维护的过程很繁琐。

假设每个点最上、最左、最右的点指针分别为 \(a,b,c\)\(\theta\) 表示两向量之间的夹角大小,用 \(\vec{x} \cdot \vec{y}\) 表示两个向量的点积,即 \(\vec{x} \cdot \vec{y} = \left| \vec{x} \right| \left| \vec{y} \right| \cos \theta\),用 \(\vec{x} \cdot \vec{y}\) 表示两个向量的叉积,即 \(\vec{x} \times \vec{y} = \left| \vec{x} \right| \left| \vec{y} \right| \sin \theta\)

  • 对于某个向量的最上的点,就像旋转卡壳的板子那样用叉积判断。

因为两个向量的叉积能够用来计算以其为邻边的平行四边形的面积。

如比较上图中 \(\overrightarrow{DE} \times \overrightarrow{AE}\)\(\overrightarrow{DE} \times \overrightarrow{BE}\) 的大小,即可知对于向量 \(\overrightarrow{DE}\),点 \(B\) 相对于点 \(A\) 更上,所以此时指针更新到 \(B\)

  • 对于某个向量的最左和最右的点,则要用点积判断。

因为两个向量的点积能够用于计算向量端点的 \(x\) 轴上的距离。

比如图中的 \(\overrightarrow{DE} \cdot \overrightarrow{AE}\),其值为 \(\left| \overrightarrow{DE} \right| \left| \overrightarrow{HE} \right|\),而 \(\left| \overrightarrow{HE} \right|\) 正好是当前向量到点 \(A\)\(x\) 轴上的距离。

再比如图中的 \(\overrightarrow{ED} \cdot \overrightarrow{CD}\),其值为 \(\left| \overrightarrow{ED} \right| \left| \overrightarrow{DG} \right|\),而 \(\left| \overrightarrow{DG} \right|\) 正好是当前向量到点 \(C\)\(x\) 轴上的距离。

要注意这里求出来的点积要除以当前向量的长度。

  • 矩形的面积可以用之前在旋转卡壳时的数据求出。

以图中情形为例。

矩形的长就是 \(\dfrac{\overrightarrow{DE} \cdot \overrightarrow{AE} + \overrightarrow{ED} \cdot \overrightarrow{CD}}{\left| \overrightarrow{ED} \right| } + \left| \overrightarrow{ED} \right|\)

矩形的高就是 $\dfrac{\overrightarrow{DE} \times \overrightarrow{BE}}{\left| \overrightarrow{ED} \right|} $。

  • 然后是如何求矩形四个点的坐标。

比如求图中的 \(G\) 点坐标,其实就是点 \(D\) 的坐标加上向量 \(\overrightarrow{DG}\),具体如何求 \(\overrightarrow{DG}\) 可以看前面。

最后就是精度问题,此题没有 SPJ,为了防止输出 -0.00000,得特判一下。

code

#include <bits/stdc++.h>
using namespace std;
const int N=1e5;const double eps=1e-8;
struct d{double x,y;d(double a=0,double b=0):x(a),y(b){}}p[N],s[N],q[N];
int n,tp,id;typedef d vec;double k1,k2;
vec operator+(vec a,vec b){return vec(a.x+b.x,a.y+b.y);}vec operator-(d a,d b){return vec(a.x-b.x,a.y-b.y);}
vec operator*(vec a,double p){return vec(a.x*p,a.y*p);}vec operator/(vec a,double p){return vec(a.x/p,a.y/p);}
double dis(d a){return sqrt(a.x*a.x+a.y*a.y);}void kj(int a){if(fabs(s[a].x)<=eps)s[a].x=0;if(fabs(s[a].y)<=eps)s[a].y=0;}
double cj(d a,d b){return a.x*b.y-a.y*b.x;}double dj(d a,d b){return a.x*b.x+a.y*b.y;}
bool cmp(d a,d b){k1=atan2((a.y-p[1].y),(a.x-p[1].x));k2=atan2((b.y-p[1].y),(b.x-p[1].x));if(k1==k2)return a.x<b.x;return k1<k2;}
void xzqq(){
    double ans=1e114,l,r,h,e,len;int a=2,b=2,c=2;
    for(int i=1;i<=tp;i++)s[i+tp]=s[i];
    for(int i=2;i<=tp+1;i++){
	while(cj(s[a]-s[i],s[i-1]-s[i])<=cj(s[a+1]-s[i],s[i-1]-s[i]))a++;
	while(dj(s[b]-s[i],s[i-1]-s[i])>=dj(s[b+1]-s[i],s[i-1]-s[i]))b++;
	if(i==2) c=a;while(dj(s[c]-s[i-1],s[i]-s[i-1])>=dj(s[c+1]-s[i-1],s[i]-s[i-1]))c++;
	len=dis(s[i]-s[i-1]);l=dj(s[c]-s[i],s[i-1]-s[i])/len;r=dj(s[b]-s[i-1],s[i]-s[i-1])/len;
	h=cj(s[i]-s[i-1],s[i]-s[a])/len;l=fabs(l);r=fabs(r);h=fabs(h);e=(l+r-len)*h;
	if(e<ans){
	    ans=e;q[1]=s[i]-(s[i]-s[i-1])*(l/len);q[2]=q[1]+(s[i]-s[i-1])*((r+l-len)/len);
            q[3]=q[2]+(s[b]-q[2])*(h/dis(s[b]-q[2]));q[4]=q[3]+(s[i-1]-s[i])*((r+l-len)/len); 
	}
    }printf("%.5lf\n",ans);
}int main(){
    cin>>n;for(int i=1;i<=n;i++){
	scanf("%lf%lf",&p[i].x,&p[i].y);if(i==1)continue;
	if(p[i].y<p[1].y||(p[i].y==p[1].y&&fabs(p[i].x)<fabs(p[1].x)))swap(p[1],p[i]);
    }sort(p+2,p+1+n,cmp);s[tp=1]=p[1];
    for(int i=2;i<=n;i++){while(tp>1&&cj(s[tp]-s[tp-1],p[i]-s[tp])<=0)tp--;s[++tp]=p[i];}xzqq();
    q[0].y=1e9;for(int i=1;i<=4;i++)if(q[i].y<q[id].y||(q[i].y==q[id].y&&q[i].x<q[id].x))id=i;
    for(int i=1;i<=4;i++)kj(i);printf("%.5lf %.5lf\n",q[id].x+eps,q[id].y+eps);    
    for(int i=0;i<3;i++)printf("%.5lf %.5lf\n",q[(i+id)%4+1].x+eps,q[(i+id)%4+1].y+eps);return 0;
}
posted @ 2022-03-16 21:30  AIskeleton  阅读(30)  评论(0编辑  收藏  举报