2142. 最小矩形覆盖
题目链接
2142. 最小矩形覆盖
已知平面上不共线的一组点的坐标,求覆盖这组点的面积最小的矩形。
输出矩形的面积和四个顶点的坐标。
输入格式
第一行包含一个整数 \(n\),表示点的数量。
接下来 \(n\) 行,每行包含两个用空格隔开的浮点数,表示一个点的 \(x\) 坐标和 \(y\) 坐标。
不用科学计数法,但如果小数部分为 \(0\),则可以写成整数。
输出格式
共 \(5\) 行,第 \(1\) 行输出一个浮点数,表示所求得的覆盖输入点集的最小矩形的面积。
接下来 \(4\) 行,每行包含两个用空格隔开的浮点数,表示所求矩形的一个顶点的 \(x\) 坐标和 \(y\) 坐标。
先输出 \(y\) 坐标最小的顶点的 \(x,y\) 坐标,如果有两个点的 \(y\) 坐标同时达到最小,则先输出 \(x\) 坐标较小者的 \(x,y\) 坐标。
然后,按照逆时针的顺序输出其他三个顶点的坐标。
不用科学计数法,精确到小数点后 \(5\) 位,后面的 \(0\) 不可省略。
答案不唯一,输出任意一组正确结果即可。
数据范围
\(3 \le n \le 50000\)
输入样例:
6
1.0 3.00000
1 4.00000
2.00000 1
3 0.00000
3.00000 6
6.0 3.0
输出样例:
18.00000
3.00000 0.00000
6.00000 3.00000
3.00000 6.00000
0.00000 3.00000
解题思路
旋转卡壳
先求出凸包,借助旋转卡壳算法,对于一条直线来说,找到距离直线最远的点,如果该直线是最终答案的某条直线的话,则经过最远点且和该直线平行的直线也一定是最终答案的某条直线,其余两条直线往中间缩即可确定出来,且同样具有单调性,同样可以用双指针算法
- 时间复杂度:\(O(nlogn)\)
代码
// 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
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
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;
}