题意:给你n个点,让你找到一个面积最小的矩形覆盖住所有的点,输出最小矩形面积和矩形四个顶点的坐标。
题解:
首先把所有点覆盖其实只需要把凸包上的所有点覆盖就行了,因为其他点已经被凸包上的点覆盖了。然后我看了别人的题解,似乎是可以证明,最优的情况矩形一定会有一条边与凸包上的一条边重合。当然也可以通过大胆猜想与直观感知来得到这一结论。
有了这个结论,我们就考虑枚举凸包上的边,我们确定了一条边之后,我们为了方便描述,看作把凸包旋转成这条边位于凸包下侧的情况。那么我们相当于找到了矩形下面的那条边,需要找的是左侧、右侧和上侧的边。左侧的边肯定要覆盖最靠左的点,右侧要覆盖最靠右的点,上侧要覆盖最靠上的点,那么我们要先找到这三个点。由于点积的几何意义是一个向量在另一个向量上的投影,所以我们只需要找点积最小和点积最大的两个点,最小的就是最左侧的,最大的就是最右侧的。至于最上分的,直接用叉积找,叉积最大的是最远的,因为相当于是一个底固定,高不固定的平行四边形面积,显然面积越大高越大,找到的点就离点越远。找到其他三个点之后,我们可以通过一些向量的运算来算出矩形的四个顶点的坐标和矩形的面积,具体可以看代码,都不难理解。
但是每次都这样做一遍复杂度不对啊,然后我们发现,枚举下一条边时左侧、右侧、上侧最远点都是单调的,于是可以旋转卡壳,这样最终复杂度就是 O(nlogn) O ( n l o g n ) 了,复杂度瓶颈在于给点排序,其他操作应该都是 O(n) O ( n ) 的。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,tp,ji;
double eps=1e-12,ans=2e9,d,ld,rd,w,h;
struct Point
{
double x,y;
}a[500010],sta[500010],res[10];
struct Vector
{
double x,y;
};
int cmp(Point x,Point y)
{
if(x.y==y.y)
return x.x<y.x;
return x.y<y.y;
}
double chaji(Vector x,Vector y)
{
return x.x*y.y-x.y*y.x;
}
double dianji(Vector x,Vector y)
{
return x.x*y.x+x.y*y.y;
}
double dis(Point x,Point y)
{
return sqrt((x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y));
}
int dcmp(double x)
{
if(fabs(x)<eps)
return 0;
if(x>0)
return 1;
return -1;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%lf%lf",&a[i].x,&a[i].y);
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;++i)
{
Vector x,y;
while(tp>1)
{
x.x=sta[tp].x-sta[tp-1].x;
x.y=sta[tp].y-sta[tp-1].y;
y.x=a[i].x-sta[tp-1].x;
y.y=a[i].y-sta[tp-1].y;
if(dcmp(chaji(x,y))<=0)
--tp;
else
break;
}
sta[++tp]=a[i];
}
ji=tp;
for(int i=n-1;i>=1;--i)
{
Vector x,y;
while(tp>ji)
{
x.x=sta[tp].x-sta[tp-1].x;
x.y=sta[tp].y-sta[tp-1].y;
y.x=a[i].x-sta[tp-1].x;
y.y=a[i].y-sta[tp-1].y;
if(dcmp(chaji(x,y))<=0)
--tp;
else
break;
}
sta[++tp]=a[i];
}
if(n>1)
--tp;
int l=2,r=2,t=2;
for(int i=1;i<=tp;++i)
{
d=dis(sta[i],sta[i+1]);
Vector x,y,z;
while(1)
{
x.x=sta[i+1].x-sta[i].x;
x.y=sta[i+1].y-sta[i].y;
y.x=sta[t+1].x-sta[i].x;
y.y=sta[t+1].y-sta[i].y;
z.x=sta[t].x-sta[i].x;
z.y=sta[t].y-sta[i].y;
if(dcmp(chaji(x,y)-chaji(x,z))>=0)
t=t%tp+1;
else
break;
}
if(i==1)
r=t;
while(1)
{
x.x=sta[i+1].x-sta[i].x;
x.y=sta[i+1].y-sta[i].y;
y.x=sta[l+1].x-sta[i].x;
y.y=sta[l+1].y-sta[i].y;
z.x=sta[l].x-sta[i].x;
z.y=sta[l].y-sta[i].y;
if(dcmp(dianji(x,y)-dianji(x,z))>=0)
l=l%tp+1;
else
break;
}
while(1)
{
x.x=sta[i+1].x-sta[i].x;
x.y=sta[i+1].y-sta[i].y;
y.x=sta[r+1].x-sta[i].x;
y.y=sta[r+1].y-sta[i].y;
z.x=sta[r].x-sta[i].x;
z.y=sta[r].y-sta[i].y;
if(dcmp(dianji(x,y)-dianji(x,z))<=0)
r=r%tp+1;
else
break;
}
x.x=sta[i+1].x-sta[i].x;
x.y=sta[i+1].y-sta[i].y;
y.x=sta[l].x-sta[i].x;
y.y=sta[l].y-sta[i].y;
ld=fabs(dianji(x,y)/d);
y.x=sta[r].x-sta[i].x;
y.y=sta[r].y-sta[i].y;
rd=fabs(dianji(x,y)/d);
w=ld+rd;
y.x=sta[t].x-sta[i].x;
y.y=sta[t].y-sta[i].y;
h=fabs(chaji(x,y))/d;
if(w*h<ans)
{
ans=w*h;
res[1].x=sta[i].x+(sta[i+1].x-sta[i].x)*(ld/d);
res[1].y=sta[i].y+(sta[i+1].y-sta[i].y)*(ld/d);
res[2].x=res[1].x+(sta[l].x-res[1].x)*(h/dis(sta[l],res[1]));
res[2].y=res[1].y+(sta[l].y-res[1].y)*(h/dis(sta[l],res[1]));
res[3].x=res[2].x+(sta[t].x-res[2].x)*(w/dis(res[2],sta[t]));
res[3].y=res[2].y+(sta[t].y-res[2].y)*(w/dis(res[2],sta[t]));
res[4].x=res[3].x+(sta[r].x-res[3].x)*(h/dis(sta[r],res[3]));
res[4].y=res[3].y+(sta[r].y-res[3].y)*(h/dis(sta[r],res[3]));
}
}
printf("%.5lf\n",ans);
int ji=1;
for(int i=2;i<=4;++i)
{
if(res[i].y<res[ji].y)
ji=i;
else if(res[i].y==res[ji].y&&res[i].x<res[ji].x)
ji=i;
}
for(int i=5;i<=8;++i)
res[i]=res[i-4];
for(int i=0;i<=3;++i)
{
if(res[ji+i].x<eps)
res[ji+i].x=0.0;
if(res[ji+i].y<eps)
res[ji+i].y=0.0;
printf("%.5lf %.5lf\n",res[ji+i].x,res[ji+i].y);
}
return 0;
}