hdu 5251 矩形面积 (旋转卡壳求最小面积外接矩形)

矩形面积

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 417    Accepted Submission(s): 239


Problem Description
小度熊有一个桌面,小度熊剪了很多矩形放在桌面上,小度熊想知道能把这些矩形包围起来的面积最小的矩形的面积是多少。
 

Input
第一行一个正整数 T,代表测试数据组数( 1T20 ),接下来 T 组测试数据。

每组测试数据占若干行,第一行一个正整数 N(1N<≤1000) ,代表矩形的数量。接下来 N 行,每行 8 个整数 x1,y1,x2,y2,x3,y3,x4,y4 ,代表矩形的四个点坐标,坐标绝对值不会超过10000。
 

Output
对于每组测试数据,输出两行:

第一行输出"Case #i:",i 代表第 i 组测试数据。
第二行包含1 个数字,代表面积最小的矩形的面积,结果保留到整数位。
 

Sample Input
  
  
2 2 5 10 5 8 3 10 3 8 8 8 8 6 7 8 7 6 1 0 0 2 2 2 0 0 2
 

Sample Output
  
  
Case #1: 17 Case #2: 4
 

Source
 

Recommend
hujie   |   We have carefully selected several similar problems for you:   6010  6009  6008  6007  6006 
 

Statistic |  Submit |  Discuss | Note

题解:旋转卡壳求最小面积外接矩形

先对所以矩形的顶点求凸包,然后考虑求凸包的最小面积外接矩形。

有一点很显然就是外接矩形的某条边上一定包含至少两个凸包上的点,其他边至少包含一个。这样才能确定出一个合法的矩形。

所以我们考虑枚举凸包上的相邻两点,确定矩形的一条边所在的直线。然后就是要求到该直线最高,最左,最右的点。这些点随着凸包上边的旋转是单调的。

最高的好判断,直接用点到直线的距离。

最左最右的话,可以用点积,为什么是点积,因为我们要求的两个点一定在该直线上的投影最长,注意向量的方向。

那么矩形的宽也可以用计算投影的方式统计出来。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 10003
#define eps 1e-7
using namespace std;
struct vector{
	double x,y;
	vector (double X=0,double Y=0){
		x=X,y=Y;
	}
}a[N],ch[N];
int n,m,cnt;
double ans;
typedef vector point;
vector operator -(vector a,vector b){
	return vector (a.x-b.x,a.y-b.y);
}
vector operator +(vector a,vector b){
    return vector (a.x+b.x,a.y+b.y);
}
vector operator *(vector a,double t){
	return vector (a.x*t,a.y*t);
}
bool operator <(vector a,vector b){
	return a.x<b.x||a.x==b.x&&a.y<b.y;
}
double cross(vector a,vector b){
	return a.x*b.y-a.y*b.x;
}
double dot(vector a,vector b){
	return a.x*b.x+a.y*b.y;
}
void convexhull()
{
	sort(a+1,a+n+1);
	if (n==1) {
		ch[m++]=a[1];
		return;
	}
	m=0;
	for (int i=1;i<=n;i++){
		while (m>1&&cross(ch[m-1]-ch[m-2],a[i]-ch[m-2])<=0) m--;
		ch[m++]=a[i];
	}
	int k=m;
	for (int i=n-1;i>=1;i--){
		while (m>k&&cross(ch[m-1]-ch[m-2],a[i]-ch[m-2])<=0) m--;
		ch[m++]=a[i];
	}
	m--;
}
int dcmp(double x)
{
	if (fabs(x)<eps) return 0;
	else return x<0?-1:1;
}
double len(vector a)
{
	return sqrt(a.x*a.x+a.y*a.y);
}
double distl(point p,point a,point b)
{
	vector v=p-a; vector u=b-a;
	return fabs(cross(v,u))/len(u);
}
double calc(point a,point b,point c)
{
	double t=dot(a-b,c-a);
	t/=len(a-b); t/=len(c-a);
	return acos(t);
}
void rotating()
{
	ans=1e16;
	int i=1,j=1,k=1;
	for (int t=2;t<m;t++)
	 if (distl(ch[t],ch[0],ch[1])>=distl(ch[k],ch[0],ch[1])||t==2)k=t;
	for (int t=2;t<m;t++)
	 if (dot(ch[1]-ch[0],ch[t]-ch[1])>=dot(ch[1]-ch[0],ch[i]-ch[1])||t==2) 
	   i=t;
    for (int t=2;t<m;t++)
	 if (dot(ch[0]-ch[1],ch[t]-ch[0])>=dot(ch[0]-ch[1],ch[j]-ch[0])||t==2) // ch[0]-ch[1] ch[t]-ch[1] 的点积最大,那么投影在向量上的长度就最大。 
	   j=t;
	//cout<<dot(ch[1]-ch[0],ch[2]-ch[0])<<" "<<dot(ch[1]-ch[0],ch[3]-ch[0])<<endl;
	//cout<<k<<" "<<i<<" "<<j<<endl;
	double h=distl(ch[k],ch[0],ch[1]);
	double l=len(ch[0]-ch[1])+fabs(dot(ch[1]-ch[0],ch[i]-ch[1]))/len(ch[0]-ch[1])+fabs(dot(ch[0]-ch[1],ch[j]-ch[0]))/len(ch[0]-ch[1]);
	ans=min(ans,h*l);
	ch[m]=ch[0];
	for (int t=1;t<m;t++){
		while (distl(ch[k],ch[t],ch[t+1])<=distl(ch[(k+1)%m],ch[t],ch[t+1])) 
		 k=(k+1)%m;
		if (i==t+1) i=(i+1)%m;
		while (dot(ch[t+1]-ch[t],ch[i]-ch[t+1])<=dot(ch[t+1]-ch[t],ch[(i+1)%m]-ch[t+1])) 
		  i=(i+1)%m;
		while (dot(ch[t]-ch[t+1],ch[j]-ch[t])<=dot(ch[t]-ch[t+1],ch[(j+1)%m]-ch[t])) 
		  j=(j+1)%m;
		if (j==t) j=((j-1)%m+m)%m;
		h=distl(ch[k],ch[t],ch[t+1]);
		l=len(ch[t]-ch[t+1])+fabs(dot(ch[t+1]-ch[t],ch[i]-ch[t+1]))/len(ch[t]-ch[t+1])+fabs(dot(ch[t]-ch[t+1],ch[j]-ch[t]))/len(ch[t]-ch[t+1]);
		ans=min(ans,h*l);
	}
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	int t; scanf("%d",&t);
	for (int T=1;T<=t;T++){
		scanf("%d",&n); cnt=0;
		for (int i=1;i<=n;i++){
			for (int j=0;j<4;j++)
			 ++cnt,scanf("%lf%lf",&a[cnt].x,&a[cnt].y);
		} 
        n=cnt;
        convexhull();
        //for (int i=0;i<m;i++) cout<<ch[i].x<<" "<<ch[i].y<<endl;
        rotating();
        printf("Case #%d:\n",T);
        //printf("%.4lf\n",ans);
        printf("%.0lf\n",ans);
	}
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值