UVA 10173 最小矩形覆盖(凸包+旋转卡壳)


传送门


题意正如标题。

可为什么用旋转卡壳呢?

先把有的点用一个凸包维护,那么这个凸包一定覆盖了所有的点,如果要有一个矩形覆盖所有的点,

那么他一定有一条边和凸包的一条边重合。


#include<bits/stdc++.h>
#define eps 1e-8
using namespace  std;

const int _max = 1e3 + 10;
const double PI = acos(-1);
int n;

int sgn(double x) //三态函数
{
	if(fabs(x)<eps) return 0;
	else return x<0?-1:1;
}

struct point
{
	double x,y;
} p[_max],res[_max];

bool mult(point sp,point ep,point op)
{
	return (sp.x - op.x) * (ep.y - op.y)
	       >= (ep.x - op.x) * (sp.y - op.y);
}

bool operator < (const point &l, const point &r)
{
	return l.y < r.y ||(l.y == r.y && l.x < r.x);
}

int graham(point pnt[],int n, point res[]) //构造凸包
{
	int i,len ,k = 0,top = 1;
	sort(pnt,pnt+n);
	if(n == 0)return 0;
	res[0] = pnt[0];
	if(n == 1)return 1;
	res[1] = pnt[1];
	if(n == 2)return 2;
	res[2] = pnt[2];
	for(int i =2; i < n; ++ i)
	{
		while(top && mult(pnt[i],res[top],res[top-1]))
			top--;
		res[++top] = pnt[i];
	}
	len = top;
	res[++top] = pnt[n - 2];
	for(i = n - 3; i >= 0; -- i)
	{
		while(top!=len && mult(pnt[i],res[top],res[top-1]))
			top--;
		res[++top] = pnt[i];
	}
	return top;//返回凸包中点的个数
}

double len(point A,point B) //返回向量AB的模平方
{
	return (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y);
}

double dot(point A,point B,point C) //点乘
{
	return (C.x-A.x)*(B.x-A.x)+(C.y-A.y)*(B.y-A.y);
}

double cross(point A,point B,point C) //叉乘
{
	return (B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x);
}

double minRetangleCover() //最小矩形面积覆盖(旋转卡壳)
{
	if(n < 3) return 0.0;
	res[n] = res [0];
	double ans = -1;
	int r = 1, p = 1,q;
	for(int i = 0; i < n; ++ i)
	{
		//卡出离边 res[i]-res[i+1]最远的点
		while(sgn(cross(res[i],res[i+1],res[r+1])-cross(res[i],res[i+1],res[r]))>=0)
			r = (r+1)% n;
		//卡出res[i]-res[i+1]方向上正向n最远的点
		while(sgn(dot(res[i],res[i+1],res[p+1])-dot(res[i],res[i+1],res[p]))>=0)
			p = (p+1)% n;
		if(i == 0) q = p;
		//卡出res[i]-res[i+1]方向上负向最远的点
		while(sgn(dot(res[i],res[i+1],res[q+1])-dot(res[i],res[i+1],res[q]))<=0)
			q = (q+1)% n;
		double d = len(res[i],res[i+1]);
		double temp = cross(res[i],res[i+1],res[r])*(dot(res[i],res[i+1],res[p])-dot(res[i],res[i+1],res[q]))/d;
		if(ans < 0 || ans > temp) ans = temp;
	}
	return ans;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
#endif // ONLINE_JUDGE
	while(scanf("%d",&n)==1 && n)
	{
		for(int i = 0; i < n; ++ i)
		{
			scanf("%lf%lf",&p[i].x,&p[i].y);
		}
		n = graham(p,n,res);//构造凸包
		printf("%.4f\n",minRetangleCover());
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值