HDU 5251 矩形面积(百度之星初赛 #1 1006)

题面:

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
 

思路:

看第一眼好像没什么思路……好像最近看题都是这样QAQ

其实就是要求把给出的矩形的所有顶点都包进去的矩形的最小面积,也就是把包含所有矩形顶点的凸包覆盖的矩形面积。

然后就很清楚了,求所有顶点的凸包,然后求能完全覆盖凸包的矩形的最小面积。

求一些点的集合的凸包,有两种方法:

1.Graham扫描法,时间复杂度O(nlogn)

2.Jarvis步进法,时间复杂度O(nh), h为凸包上的顶点数

这里用第一种方法求。大概的过程是这样:

选出所有点中最左下的点(记作P0),然后把这个点看成原点,把所有其他点按照极角从小到大排序(也就是按逆时针方向排序)。极角相同的点,按照距离从小到大排序。

将P0以及排好序的序列中前两个点入栈。每次将一个顶点入栈之前,看新加入这个顶点会不会让要求的凸包变成凹多边形,会的话把栈顶出栈,直到可以组成凸多边形


求出凸包以后,就可以求矩形的面积了。可以参考UVA 10173 / HNOI 2007

遍历凸包的每条边,求矩形的一条边与这条边共线时,最小覆盖矩形的面积,时间复杂度为O(n*n)

//竟然在这里脑残了好久orz……多年不做几何题……


这个题目也可以当作求凸包和求最小覆盖矩形的模板


样例画出来是这样的:


绿色的部分是凸包,橙色部分是与凸包某一条边共线并且覆盖整个凸包的矩形


代码:

#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <algorithm>
#include <stack>
using namespace std;

const double eps = 1e-7;
const double PI = acos(-1.0);

//点类
class Point{
public:
	double x, y;
	Point(double _x, double _y) :x(_x), y(_y){};
	Point(){};

	//与另一个点的距离
	double getLen(Point& a){
		return sqrt((x - a.x)*(x - a.x) + (y - a.y)*(y - a.y));
	}

	//与另一个点所连直线的倾斜角
	//返回值在0-PI之间
	double getAngle(Point& a){
		if (fabs(a.x - x) < eps && fabs(a.y - y) >= eps)
			return PI / 2;
		if (fabs(a.x - x) < eps && fabs(a.y - y) < eps)
			return 0;
		double angle = atan((y - a.y) / (x - a.x));
		if (angle < 0.0)
			return angle + PI;
		return angle;
	}

	//求点与原点的连线与x轴正方向的夹角,返回值在0-2*PI之间
	double getAbsAngle(){
		if (fabs(x) < eps && fabs(y) < eps)
			return 0;
		if (fabs(x) < eps && y > 0)
			return PI / 2;
		if (fabs(x) < eps && y < 0)
			return PI * 3 / 2;
		double an = atan(y / x);
		if (x < 0){
			return PI + an;
		}
		return an;
	}
};

//多边形类
class Polygon{
public:
	Point p[4096];
	int size;

	//偷个懒……可以直接写下标
	Point& operator[](int index){
		return p[index];
	}
};

//根据极角排序的时候用的结构体
struct node{
	Point p;
	double a;
	double dis;
	bool operator < (const node& b){
		if (fabs(a - b.a) < eps)
			return dis < b.dis;
		return a < b.a;
	}
};

//求叉积
double Multiply(Point p1, Point p2, Point p3)
{
	return ((p2.x - p1.x)*(p3.y - p1.y) - (p2.y - p1.y)*(p3.x - p1.x));
}

//向量OA和向量OB的夹角
double getAngle(Point a, Point o, Point b){
	a.x -= o.x; a.y -= o.y;
	b.x -= o.x; b.y -= o.y;
	double res = fabs(a.getAbsAngle() - b.getAbsAngle());
	return res;
}

//求多边形的凸包
Polygon getConvexHull(Polygon p){
	node a[4096]; Point p0 = p[0];
	int index0 = 0;
	for (int i = 0; i < p.size; i++){
		a[i].p = p[i];
		if ((p[i].y < p0.y) || (fabs(p[i].y - p0.y) < eps && p[i].x < p0.x)){
			p0 = p[i];
			index0 = i;
		}
	}
	for (int i = 0; i < p.size; i++){
		if (i == index0){
			a[i].a = 0.0;
			continue;
		}
		a[i].a = p0.getAngle(p[i]);
		a[i].dis = p0.getLen(p[i]);
	}
	//一定要把p0也就是最左下的点放到最前面,然后对后面的点排序!!
	swap(a[0], a[index0]);
	sort(a + 1, a + p.size);

	Point res[4096]; int top = 3;
	res[0] = a[0].p; res[1] = a[1].p; res[2] = a[2].p;
	for (int i = 3; i < p.size; i++){
		while (Multiply(res[top - 2], res[top - 1], a[i].p) <= 0){
			top--;
		}
		res[top++] = a[i].p;
	}
	Polygon conv;
	conv.size = top;
	for (int i = 0; i < top; i++){
		conv[i] = res[i];
	}
	return conv;
}

//求最小覆盖矩形的面积
int getMinArea(Polygon p){
	double minArea = 0.0;
	bool set = false;

	for (int i = 0; i < p.size; i++){
		int i1 = (i + 1) % p.size;
		Point a = p.p[i % p.size], b = p.p[i1];
		double len = a.getLen(b);

		double maxd = 0.0, //过每个顶点作与ab平行的直线,平行线与直线ab的最远距离,也就是矩形的一条边长
			minl = 0.0, maxl = 0.0; //将每个点投影到直线ab上,投影在a点两侧距a点的距离

		vector<double> ls;
		for (int j = 0; j < p.size; j++){
			if (j == i || j == i1)continue;
			double angle = getAngle(b, a, p[j]);
			double d = sin(angle) * a.getLen(p.p[j]);
			maxd = max(fabs(d), maxd);

			double l = cos(angle) * a.getLen(p.p[j]);
			ls.push_back(l);
		}
		sort(ls.begin(), ls.end());
		minl = ls[0];
		maxl = ls[ls.size() - 1];
		if (!set){
			minArea = maxd * fabs(minl - maxl);
			set = 1;
		}
		else
			minArea = min(minArea, maxd * fabs(minl - maxl));
	}
	int res = (int)(minArea + 0.5);
	return res;
}

int main(){
	int _, __ = 1;
	cin >> _;
	while (_--){
		int n;
		cin >> n;
		Polygon p;
		p.size = n * 4;
		for (int i = 0; i < n * 4; i++){
			scanf("%lf%lf", &p[i].x, &p[i].y);
		}
		p = getConvexHull(p);
		printf("Case #%d:\n", __++);
		cout << getMinArea(p) << endl;
	}
#ifndef ONLINE_JUDGE
	system("pause");
#endif
	return 0;

}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值