【BZOJ1185】[HNOI2007]最小矩形覆蓋(凸包,旋轉卡殼)

#【BZOJ1185】[HNOI2007]最小矩形覆蓋(凸包,旋轉卡殼)php

題面

BZOJ 洛谷ios

題解

最小的矩形必定存在一條邊在凸包上,那麼枚舉這條邊,咱們還差三個點,即距離當前邊的最遠點,以及作這條邊的垂線的最靠左和最靠右的兩個點。 最遠點很容易求,叉積計算面積來比就行了。 那麼剩下兩個點呢? 好比說找右側的那個點,咱們僞裝當前枚舉出來的這條邊就是水平線,那麼只要當前的點和下一個點的直線與$x$軸正半軸夾角小於$90°$ 顯然就往這個方向走。而後從水平線換到通常的狀況,也就是和枚舉的這條邊的夾角小於$\frac{\pi}{2}$,點積的計算除了座標計算以外,還有$\vec{a}\dot{}\vec{b}=|\vec{a}|*|\vec{b}|*cos<\vec{a},\vec{b}>$,這樣子能夠很容易求出兩個向量之間的夾角關係,而$\alpha\le \frac{\pi}{2}$換成三角函數之間的關係就是$cos\alpha\ge 0$,所以找這個關係只須要很簡單的判斷兩個向量之間的點積是否大於等於$0$。 同理,考慮如何找最靠左的點,那麼就是兩個向量的夾角範圍在$\frac{\pi}{2}$以上,即點積小於$0$。 經過這個方法,彷佛能夠求解已知全部夾角的$n$邊形覆蓋,只須要旋轉卡殼的時候依次考慮每一個點,而每一個點是否可否移動到下個點的條件與夾角相關,而夾角的信息能夠經過點積獲得。函數

而後這題就是卡精度卡精度卡精度之類的spa

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define double long double
#define MAX 50050
const double eps=1e-10;
const double Pi=acos(-1);
struct Point{double x,y,ang;};
bool operator<(Point a,Point b){return (a.ang!=b.ang)?a.ang<b.ang:a.x<b.x;}
Point operator+(Point a,Point b){return (Point){a.x+b.x,a.y+b.y};}
Point operator-(Point a,Point b){return (Point){a.x-b.x,a.y-b.y};}
Point operator*(Point a,double b){return (Point){a.x*b,a.y*b};}
Point operator/(Point a,double b){return (Point){a.x/b,a.y/b};}
double operator*(Point a,Point b){return a.x*b.x+a.y*b.y;}
double Cross(Point a,Point b){return a.x*b.y-a.y*b.x;}
double Len(Point a){return sqrt(a.x*a.x+a.y*a.y);}
double Dis(Point a,Point b){return Len(a-b);}
Point Rotate(Point p,double a){double c=cos(a),s=sin(a);return (Point){p.x*c-p.y*s,p.x*s+p.y*c};}
Point S[MAX],Ans[10];int top;
void Graham(Point *p,int n)
{
	int pos=1;
	for(int i=2;i<=n;++i)
		if(p[i].x<p[pos].x||(p[i].x==p[pos].x&&p[i].y<p[pos].y))
			pos=i;
	swap(p[1],p[pos]);
	for(int i=2;i<=n;++i)p[i].ang=atan2(p[i].y-p[1].y,p[i].x-p[1].x);
	sort(&p[2],&p[n+1]);S[++top]=p[1];S[++top]=p[2];
	for(int i=3;i<=n;++i)
	{
		while(top>2&&Cross(p[i]-S[top],p[i]-S[top-1])>=0)--top;
		S[++top]=p[i];
	}
}
struct Line{Point a,v;};
Point Intersection(Line a,Line b)
{
	Point c=b.a-a.a;
	double t=Cross(b.v,c)/Cross(b.v,a.v);
	return a.a+a.v*t;
}
int n;double ans=1e18;
Point p[MAX],tmp[5];
void ScanLine(int n)
{
	S[n+1]=S[1];S[0]=S[n];
	for(int i=1,j1=3,j2=3,j3=n;i<=n;++i)
	{
		if(i==1)while((S[i]-S[i+1])*(S[j3-1]-S[j3])>0)j3=(j3==1)?n:j3-1;
		while(Cross(S[j1]-S[i],S[j1]-S[i+1])<=Cross(S[j1+1]-S[i],S[j1+1]-S[i+1]))j1=(j1==n)?1:j1+1;
		while((S[i+1]-S[i])*(S[j2+1]-S[j2])>0)j2=(j2==n)?1:j2+1;
		while((S[i+1]-S[i])*(S[j3+1]-S[j3])<0)j3=(j3==n)?1:j3+1;
		Line l0=(Line){S[i],S[i+1]-S[i]};
		Line l1=(Line){S[j1],S[i]-S[i+1]};
		Line l2=(Line){S[j2],Rotate(S[i+1]-S[i],Pi/2)};
		Line l3=(Line){S[j3],Rotate(S[i]-S[i+1],Pi/2)};
		tmp[1]=Intersection(l0,l2);
		tmp[2]=Intersection(l2,l1);
		tmp[3]=Intersection(l1,l3);
		tmp[4]=Intersection(l3,l0);
		double area=Dis(tmp[1],tmp[2])*Dis(tmp[2],tmp[3]);
		if(area<ans)
			ans=area,Ans[1]=tmp[1],Ans[2]=tmp[2],Ans[3]=tmp[3],Ans[4]=tmp[4];
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%Lf%Lf",&p[i].x,&p[i].y);
	for(int i=1;i<=n;++i)p[i].x+=eps,p[i].y-=eps;
	Graham(p,n);
	ScanLine(top);
	printf("%.5Lf\n",ans);
	Ans[5]=Ans[1];Ans[6]=Ans[2];Ans[7]=Ans[3];Ans[8]=Ans[4];
	int pos=1;
	for(int i=2;i<=4;++i)
		if(Ans[i].y<Ans[pos].y||(Ans[i].y==Ans[pos].y&&Ans[i].x<=Ans[pos].x))
			pos=i;
	for(int i=pos;i<=pos+3;++i)printf("%.5Lf %.5Lf\n",Ans[i].x+100*eps,Ans[i].y+100*eps);
	return 0;
}
相關文章
相關標籤/搜索