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;
}
posted @ 2022-11-23 16:25  zyy2001  阅读(83)  评论(0编辑  收藏  举报