题目链接
已知平面上不共线的一组点的坐标,求覆盖这组点的面积最小的矩形。
输出矩形的面积和四个顶点的坐标。
输入格式
第一行包含一个整数 n,表示点的数量。
接下来 n 行,每行包含两个用空格隔开的浮点数,表示一个点的 x 坐标和 y 坐标。
不用科学计数法,但如果小数部分为 0,则可以写成整数。
输出格式
共 5 行,第 1 行输出一个浮点数,表示所求得的覆盖输入点集的最小矩形的面积。
接下来 4 行,每行包含两个用空格隔开的浮点数,表示所求矩形的一个顶点的 x 坐标和 y 坐标。
先输出 y 坐标最小的顶点的 x,y 坐标,如果有两个点的 y 坐标同时达到最小,则先输出 x 坐标较小者的 x,y 坐标。
然后,按照逆时针的顺序输出其他三个顶点的坐标。
不用科学计数法,精确到小数点后 5 位,后面的 0 不可省略。
答案不唯一,输出任意一组正确结果即可。
数据范围
3≤n≤50000
输入样例:
输出样例:
解题思路
旋转卡壳
先求出凸包,借助旋转卡壳算法,对于一条直线来说,找到距离直线最远的点,如果该直线是最终答案的某条直线的话,则经过最远点且和该直线平行的直线也一定是最终答案的某条直线,其余两条直线往中间缩即可确定出来,且同样具有单调性,同样可以用双指针算法
代码
// Problem: 最小矩形覆盖
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2144/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
//
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=50005;
const double eps=1e-12,inf=1e20,pi=acos(-1);
typedef pair<double,double> PDD;
int n,stk[N],top;
PDD q[N],res[4];
double min_area=inf;
PDD operator+(PDD a,PDD b)
{
return {a.fi+b.fi,a.se+b.se};
}
PDD operator-(PDD a,PDD b)
{
return {a.fi-b.fi,a.se-b.se};
}
PDD operator*(PDD a,double t)
{
return {a.fi*t,a.se*t};
}
PDD operator/(PDD a,double t)
{
return {a.fi/t,a.se/t};
}
double operator*(PDD a,PDD b)
{
return a.fi*b.se-a.se*b.fi;
}
double operator&(PDD a,PDD b)
{
return a.fi*b.fi+a.se*b.se;
}
double area(PDD a,PDD b,PDD c)
{
return (b-a)*(c-a);
}
int sign(double x)
{
if(fabs(x)<eps)return 0;
if(x<0)return -1;
return 1;
}
int dcmp(double x,double y)
{
if(fabs(x-y)<eps)return 0;
if(x<y)return -1;
return 1;
}
double get_len(PDD a)
{
return sqrt(a&a);
}
double project(PDD a,PDD b,PDD c)
{
return ((b-a)&(c-a))/get_len(b-a);
}
void andrew()
{
sort(q+1,q+1+n);
for(int i=1;i<=n;i++)
{
while(top>=2&&sign(area(q[stk[top-1]],q[stk[top]],q[i]))<=0)top--;
stk[++top]=i;
}
int k=top;
for(int i=n;i>=1;i--)
{
while(top>k&&sign(area(q[stk[top-1]],q[stk[top]],q[i]))<=0)top--;
stk[++top]=i;
}
if(n>1)top--;
}
PDD norm(PDD x)
{
return x/get_len(x);
}
PDD rotate(PDD a,double angle)
{
return {a.fi*cos(angle)+a.se*sin(angle),-a.fi*sin(angle)+a.se*cos(angle)};
}
void rotating_calipers()
{
for(int i=0;i<top;i++)stk[i]=stk[i+1];
for(int i=0,a=2,b=1,c=2;i<top;i++)
{
PDD d=q[stk[i]],e=q[stk[(i+1)%top]];
while(dcmp(area(d,e,q[stk[a]]),area(d,e,q[stk[(a+1)%top]]))<0)a=(a+1)%top;
while(dcmp(project(d,e,q[stk[b]]),project(d,e,q[stk[(b+1)%top]]))<0)b=(b+1)%top;
if(!i)c=a;
while(dcmp(project(d,e,q[stk[c]]),project(d,e,q[stk[(c+1)%top]]))>0)c=(c+1)%top;
PDD x=q[stk[a]],y=q[stk[b]],z=q[stk[c]];
double h=area(d,e,x)/get_len(e-d);
double w=((y-z)&(e-d))/get_len(e-d);
if(h*w<min_area)
{
min_area=h*w;
res[0]=d+norm(e-d)*project(d,e,y);
res[3]=d+norm(e-d)*project(d,e,z);
PDD u=norm(rotate(e-d,-pi/2));
res[1]=res[0]+u*h;
res[2]=res[3]+u*h;
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lf%lf",&q[i].fi,&q[i].se);
andrew();
rotating_calipers();
int k=0;
for(int i=0;i<4;i++)
if(dcmp(res[i].se,res[k].se)<0||!dcmp(res[i].se,res[k].se)&&dcmp(res[i].fi,res[k].fi)<0)k=i;
printf("%.5lf\n",min_area);
for(int i=k,j=0;j<4;j++,i++)
{
double x=res[i%4].fi,y=res[i%4].se;
if(!sign(x))x=0;
if(!sign(y))y=0;
printf("%.5lf %.5lf\n",x,y);
}
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!