BZOJ1185 [HNOI2007]最小矩形覆盖(旋转卡壳)

【题解】

先求出这些点的凸包 

可以证明,最小的矩形一定与凸包的边有重叠 
因此,像旋转卡壳一样,逆时针将凸包各边扫一遍,在这个过程中用向量点积、叉积维护最上点,最左点,最右点,即可 

注意这样的写法:Cross(ch[q+1]-ch[i+1],ch[i]-ch[i+1]) - Cross(ch[q]-ch[i+1],ch[i]-ch[i+1]) >-eps (不过写成 >0 也能AC。。。)


【代码】

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define eps 1e-10
struct Point
{
	double x,y;
	Point()
	{
		x=y=0;
	}
};
typedef Point Vector;
Point P[50005],ch[50005],Q[5];
Vector operator + (Vector a,Vector b)
{
	a.x+=b.x;
	a.y+=b.y;
	return a;
}
Vector operator - (Vector a,Vector b)
{
	a.x-=b.x;
	a.y-=b.y;
	return a;
}
Vector operator * (Vector a,double b)
{
	a.x*=b;
	a.y*=b;
	return a;
}
bool operator < (Point a,Point b)
{
	return a.x<b.x || ( a.x==b.x && a.y<b.y );
}
bool operator > (Point a,Point b)
{
	return a.x>b.x || ( a.x==b.x && a.y>b.y );
}
int dcmp(double x)
{
	if(x<=eps&&x>=-eps) return 0;
	if(x>0) return 1;
	return -1;
}
double Dot(Vector a,Vector b)
{
	return a.x*b.x+a.y*b.y;
}
double Cross(Vector a,Vector b)
{
	return a.x*b.y-b.x*a.y;
}
double Length(Vector a)
{
	return sqrt(a.x*a.x+a.y*a.y);
}
double dis(Point p,Point a,Point b)
{
	return fabs(Cross(p-a,b-a))/Length(a-b);
}
Vector xz(Vector a)
{
	Vector b;
	b.x=-a.y;
	b.y=a.x;
	return b;
}
Point jiaodian(Point P,Vector v,Point Q,Vector w)
{
	Vector u=P-Q;
	double t=Cross(w,u)/Cross(v,w);
	return P+v*t;
}
void kp(int low,int high)
{
	int i=low,j=high;
	Point mid=P[(i+j)/2],t;
	while(i<j)
	{
		while(P[i]<mid) i++;
		while(P[j]>mid) j--;
		if(i<=j)
		{
			t=P[i];
			P[i]=P[j];
			P[j]=t;
			i++;
			j--;
		}
	}
	if(j>low) kp(low,j);
	if(i<high) kp(i,high);
}
int main()
{
	Vector t;
	double ans=-1.0,D,H,L,R;
	int n,i,j=1,k,cnt=0,q=2,l,r=2;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		scanf("%lf%lf",&P[i].x,&P[i].y);
	kp(1,n);
	for(i=1;i<=n;i++)
	{
		while( cnt>1 && dcmp( Cross(ch[cnt]-ch[cnt-1],P[i]-ch[cnt-1]) )<=0 ) cnt--;
		ch[++cnt]=P[i];
	}
	k=cnt;
	for(i=n-1;i>=1;i--)
	{
		while( cnt>k && dcmp( Cross(ch[cnt]-ch[cnt-1],P[i]-ch[cnt-1]) )<=0 ) cnt--;
		ch[++cnt]=P[i];
	}
	//printf("(%.2lf,%.2lf) ",ch[i].x,ch[i].y);
	for(i=1;i<cnt;i++)
	{
		D=Length(ch[i]-ch[i+1]);
		while( Cross(ch[q+1]-ch[i+1],ch[i]-ch[i+1]) - Cross(ch[q]-ch[i+1],ch[i]-ch[i+1]) > -eps ) q=q%(cnt-1)+1;
		while( Dot(ch[r+1]-ch[i+1],ch[i]-ch[i+1]) - Dot(ch[r]-ch[i+1],ch[i]-ch[i+1]) < eps ) r=r%(cnt-1)+1;
		if(i==1) l=r;//否则l无法迈出"第一步" 
		while( Dot(ch[l+1]-ch[i+1],ch[i]-ch[i+1]) - Dot(ch[l]-ch[i+1],ch[i]-ch[i+1]) > -eps ) l=l%(cnt-1)+1;
		H=dis(ch[q],ch[i],ch[i+1]);
		L=Dot(ch[l]-ch[i+1],ch[i]-ch[i+1])/D;
		R=-Dot(ch[r]-ch[i+1],ch[i]-ch[i+1])/D;
		if(ans==-1||ans>H*(L+R))
		{
			ans=H*(L+R);
			t=xz(ch[i+1]-ch[i]);
			Q[1]=(ch[i]-ch[i+1])*(L/D)+ch[i+1];//从左下开始 
			Vector e=(ch[i]-ch[i+1])*((D+L)/D);
			Q[2]=(ch[i+1]-ch[i])*((D+R)/D)+ch[i];
			Q[3]=jiaodian(ch[r],t,ch[q],ch[i+1]-ch[i]);
			Q[4]=jiaodian(ch[l],t,ch[q],ch[i+1]-ch[i]);
		}
	}
	printf("%.5lf\n",ans);
	for(i=2;i<=4;i++)
		if( Q[i].y<Q[j].y || (Q[i].y==Q[j].y&&Q[i].x<Q[j].x) ) j=i;
	for(i=0;i<4;i++)
		printf("%.5lf %.5lf\n",Q[(j+i-1)%4+1].x,Q[(j+i-1)%4+1].y);
	return 0;
}


你可能感兴趣的:(凸包,旋转卡壳)