用旋转卡壳做。先求出凸包,枚举每一条边作为矩形的一边。画画图可以看出另外三个点的决策应该是单调的。对面的点用叉乘看三角形面积大小判断,两边的点用点乘看向量在已固定的边上投影的长度(即矩形的宽)判断。注意第一次的两侧定点应分别从对面定点的两侧开始判断,需要赋初值。然后用点到直线距离,两直线交点等数学运算求一求就好了。具体看代码注释吧。最后需要避免因精度问题输出-0.00000,所以整体加一个eps。
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=50010;
const double eps=0.0000001;
struct node{
double x,y;
}p1[N],ans[5];
double xx,yy,mn,a1,b1,c1,a2,b2,c2,a3,b3,c3,a4,b4,c4,h1,h2,hb,hc;
int n,top,sta[N],s,aa,bb,cc;
inline double cross(node a,node b,node c){
return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
}
inline double dot(node a,node b,node c){
return (b.x-a.x)*(c.x-a.x)+(b.y-a.y)*(c.y-a.y);
}
inline double dist(node p,double a,double b,double c){
double ans1=a*p.x+b*p.y+c;if(ans1<0)ans1=-ans1;
return ans1/sqrt(a*a+b*b);
}
bool cmp(node i,node j){
if(atan2(i.y-yy,i.x-xx)!=atan2(j.y-yy,j.x-xx))return atan2(i.y-yy,i.x-xx)<atan2(j.y-yy,j.x-xx);
return i.x<j.x;
}
inline void getpt(double ax,double bx,double cx,double ay,double by,double cy,node& p){
p.x=(cy*bx-cx*by)/(ax*by-ay*bx);(bx==0)?p.y=-ay/by*p.x-cy/by:p.y=-ax/bx*p.x-cx/bx;
}
int main(){
scanf("%d",&n);
yy=xx=1e16;
for(int i=1;i<=n;++i){
scanf("%lf%lf",&p1[i].x,&p1[i].y);
if(p1[i].y<yy||(p1[i].y==yy&&p1[i].x<xx))xx=p1[i].x,yy=p1[i].y;
}
sort(p1+1,p1+n+1,cmp);
top=1,sta[1]=1;
for(int i=2;i<=n;++i){
while(top>1&&cross(p1[sta[top-1]],p1[sta[top]],p1[i])<=0)--top;
sta[++top]=i;
}
if(top<3){
printf("0.00000\n");
if(top==1)for(int i=1;i<=4;++i)printf("%.5lf %.5lf\n",p1[sta[1]].x,p1[sta[1]].y);
else{
for(int i=1;i<=2;++i)printf("%.5lf %.5lf\n",p1[sta[1]].x,p1[sta[1]].y);
for(int i=1;i<+2;++i)printf("%.5lf %.5lf\n",p1[sta[2]].x,p1[sta[2]].y);
}
return 0;
}
sta[top+1]=sta[1],aa=bb=cc=2,mn=1e16;
for(int i=1;i<=top;++i){
while(cross(p1[sta[i+1]],p1[sta[i]],p1[sta[aa+1]])<=cross(p1[sta[i+1]],p1[sta[i]],p1[sta[aa]]))aa=aa%top+1;
if(i==1)bb=aa;
while(dot(p1[sta[i]],p1[sta[i+1]],p1[sta[bb+1]])<=dot(p1[sta[i]],p1[sta[i+1]],p1[sta[bb]]))bb=bb%top+1;
while(dot(p1[sta[i+1]],p1[sta[i]],p1[sta[cc+1]])<=dot(p1[sta[i+1]],p1[sta[i]],p1[sta[cc]]))cc=cc%top+1;
//求a到直线i,i+1的距离h1
a1=p1[sta[i+1]].y-p1[sta[i]].y,b1=p1[sta[i]].x-p1[sta[i+1]].x,
c1=p1[sta[i+1]].x*p1[sta[i]].y-p1[sta[i+1]].y*p1[sta[i]].x;
h1=dist(p1[sta[aa]],a1,b1,c1);
//构建a到i,i+1的垂线
a2=p1[sta[i]].x-p1[sta[i+1]].x,b2=p1[sta[i]].y-p1[sta[i+1]].y,
c2=p1[sta[aa]].y*(p1[sta[i+1]].y-p1[sta[i]].y)-p1[sta[aa]].x*(p1[sta[i]].x-p1[sta[i+1]].x);
//求b、c到a的垂线的距离之和hb+hc=h2
hb=dist(p1[sta[bb]],a2,b2,c2),hc=dist(p1[sta[cc]],a2,b2,c2),h2=hb+hc;
//计算面积
if(h1*h2<mn){
mn=h1*h2;
//构建过a的平行线
a2=p1[sta[i+1]].y-p1[sta[i]].y,b2=p1[sta[i]].x-p1[sta[i+1]].x,
c2=(p1[sta[i+1]].x-p1[sta[i]].x)*p1[sta[aa]].y-(p1[sta[i+1]].y-p1[sta[i]].y)*p1[sta[aa]].x;
//构建b、c到i,i+1的垂线
a3=p1[sta[i]].x-p1[sta[i+1]].x,b3=p1[sta[i]].y-p1[sta[i+1]].y,
c3=p1[sta[bb]].y*(p1[sta[i+1]].y-p1[sta[i]].y)-p1[sta[bb]].x*(p1[sta[i]].x-p1[sta[i+1]].x);
a4=p1[sta[i]].x-p1[sta[i+1]].x,b4=p1[sta[i]].y-p1[sta[i+1]].y,
c4=p1[sta[cc]].y*(p1[sta[i+1]].y-p1[sta[i]].y)-p1[sta[cc]].x*(p1[sta[i]].x-p1[sta[i+1]].x);
//计算交点,保存到ans数组
getpt(a1,b1,c1,a3,b3,c3,ans[0]),getpt(a1,b1,c1,a4,b4,c4,ans[1]),
getpt(a2,b2,c2,a4,b4,c4,ans[2]),getpt(a2,b2,c2,a3,b3,c3,ans[3]);
}
}
printf("%.5lf\n",mn);
s=0;
for(int i=1;i<=3;++i)if(ans[i].y<ans[s].y||(ans[i].y==ans[s].y&&ans[i].x<ans[s].x))s=i;
for(int i=0;i<4;++i)printf("%.5lf %.5lf\n",ans[(s+i)%4].x+eps,ans[(s+i)%4].y+eps);
return 0;
}