BZOJ 1185 HNOI 2007 最小矩形覆盖 旋转卡壳

题目大意:给出平面上的一些点,问面积最小的矩形满足覆盖所有的点。

思路:覆盖问题和不是凸包上的点没关系,先做凸包。根据贪心的思想,这个覆盖了所有点的矩形肯定至少有一条边与凸包上的边重合,那么我们枚举凸包上的每一条边,对于这个已经确定了一条边的矩形,不难确定其他三个边。注意到已知当前直线的向量,就可以求出两侧和对面的向量,而这三个向量随着枚举的边的移动是单调的,所以就可以用旋转卡壳来卡住剩下的三条边。

但是旋转卡壳时的初值会出问题,如果按照逆时针的顺序求出剩下的三条边的时候,要想通过向量直接卡第三条边的方向是不行的,因为它和第一条边正好是反过来的,所以对于同一条边来说,与第一条边的夹角和第三条边的夹角一个是顺时针另一个是逆时针。这肯定会出问题。所以要预处理出枚举的第一条边,算出三个边的位置,作为旋转卡壳的初值。可以证明,在凸包上的其他边每次旋转肯定不会超过180°,所以不会发生类似的bug。

CODE:

#define _CRT_SECURE_NO_WARNINGS

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
#define MAX 50010
using namespace std;

struct Point{
	double x,y;

	Point(double _,double __):x(_),y(__) {}
	Point() {}
	Point operator +(const Point &a)const {
		return Point(x + a.x,y + a.y);
	}
	Point operator -(const Point &a)const {
		return Point(x - a.x,y - a.y);
	}
	Point operator *(double a)const {
		return Point(x * a,y * a);
	}
	bool operator <(const Point &a)const {
		return x == a.x ? y < a.y:x < a.x;
	}
	void Read() {
		scanf("%lf%lf",&x,&y);
	}
}point[MAX],stack[MAX],ans[5];
int top;

inline double Calc(const Point &p1,const Point &p2)
{
	return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}

inline double Cross(const Point &p1,const Point &p2)
{
	return p1.x * p2.y - p1.y * p2.x;
}

struct Line{
	Point p,v;

	Line(Point _,Point __):p(_),v(__) {}
	Line() {}
};

int points;

inline bool cmp(const Point &p1,const Point &p2)
{
	return p1.y > p2.y;
}

inline void Add(const Point &p,int bottom)
{
	while(top > bottom && Cross(p - stack[top - 1],stack[top] - stack[top - 1]) >= 0)
		--top;
	stack[++top] = p;
}

inline Point GetIntersection(const Line &l1,const Line &l2)
{
	Point u = l1.p - l2.p;
	double t = Cross(l2.v,u) / Cross(l1.v,l2.v);
	return l1.p + l1.v * t;
}

double min_area = 1e15;

inline void GetArea(const Point &p1,const Point &p2,const Point &p3,const Point &p4,const Point &v1,const Point &v2,const Point &v3,const Point &v4)
{
	Line l1(p1,v1),l2(p2,v2),l3(p3,v3),l4(p4,v4);
	Point t1 = GetIntersection(l1,l2);
	Point t2 = GetIntersection(l2,l3);
	Point t3 = GetIntersection(l3,l4);
	Point t4 = GetIntersection(l4,l1);
	if(Cross(t2 - t1,t4 - t1) < min_area) {
		min_area = Cross(t2 - t1,t4 - t1);
		ans[1] = t1,ans[2] = t2,ans[3] = t3,ans[4] = t4;
	}
}

void RotatingCalipers()
{
	int p1 = 1;
	Point now = stack[2] - stack[1];
	Point r(-now.y,now.x),u(-now.x,-now.y),l(now.y,-now.x);
	while(Cross(stack[p1 + 1] - stack[p1],r) > 0)
		p1 = (p1 + 1) % top;
	int p2 = p1;
	while(Cross(stack[p2 + 1] - stack[p2],u) > 0)
		p2 = (p2 + 1) % top;
	int p3 = p2;
	while(Cross(stack[p3 + 1] - stack[p3],l) > 0)
		p3 = (p3 + 1) % top;
	for(int i = 1; i < top; ++i) {
		now = stack[i + 1] - stack[i];
		r = Point(-now.y,now.x),u = Point(-now.x,-now.y),l = Point(now.y,-now.x);
		while(Cross(stack[p1 + 1] - stack[p1],r) > 0)
			p1 = (p1 + 1) % top;
		while(Cross(stack[p2 + 1] - stack[p2],u) > 0)
			p2 = (p2 + 1) % top;
		while(Cross(stack[p3 + 1] - stack[p3],l) > 0)
			p3 = (p3 + 1) % top;
		GetArea(stack[i],stack[p1],stack[p2],stack[p3],now,r,u,l);
	}
}

int main()
{
	cin >> points;
	for(int i = 1; i <= points; ++i)
		point[i].Read();
	sort(point + 1,point + points + 1);
	int t = 1;
	while(point[t + 1].x == point[1].x)	++t;
	sort(point + 1,point + t + 1,cmp);
	stack[++top] = point[1];
	for(int i = 2; i <= points; ++i)
		Add(point[i],1);
	int temp = top;
	for(int i = points - 1; i; --i)
		Add(point[i],temp);
	stack[0] = stack[--top];
	RotatingCalipers();
	cout << fixed << setprecision(5) << min_area << endl;
	int p = 1;
	for(int i = 2; i <= 4; ++i)
		if(ans[i].y < ans[p].y || (ans[i].y == ans[p].y && ans[i].x < ans[p].x))
			p = i;
	for(int i = p; i <= 4; ++i)
		cout << fixed << setprecision(5) << ans[i].x << ' ' << ans[i].y << endl;
	for(int i = 1; i < p; ++i)
		cout << fixed << setprecision(5) << ans[i].x << ' ' << ans[i].y << endl;
	return 0;
}

时间: 2024-12-23 23:28:07

BZOJ 1185 HNOI 2007 最小矩形覆盖 旋转卡壳的相关文章

BZOJ 1185 HNOI2007 最小矩形覆盖 旋转卡壳

题目大意:最小矩形覆盖 首先有一个结论:凸包上一定有一条边与矩形的一条边重合 证明:如果不存在一条边与矩形的一条边重合,那么我将这个矩形旋转一下一定会比之前更小 于是我们枚举其中一条边,对其余三个点卡壳即可 这旋转卡壳写的真叫一个卡壳- - 还好1A掉了- - #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm>

bzoj1185 [HNOI2007]最小矩形覆盖 旋转卡壳求凸包

[HNOI2007]最小矩形覆盖 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special JudgeSubmit: 2081  Solved: 920[Submit][Status][Discuss] Description 给定一些点的坐标,要求求能够覆盖所有点的最小面积的矩形, 输出所求矩形的面积和四个顶点坐标 Input 第一行为一个整数n(3<=n<=50000) 从第2至第n+1行每行有两个浮点数,表示一个顶点的x和y坐标,不用科学计

BZOJ 1069 SCOI 2007 最大土地面积 凸包+旋转卡壳

题目大意:给出平面上的一些点,求其中四个点的最大四边形的面积. 思路:简单yy一下发现这些点肯定都在凸包上,先求个凸包.然后直接暴力肯定是不行的,我们需要一个O(n^2)的做法,比较简单的想法是枚举最后要求的四边形的一条对线,那么这个四边形就被分割成了两个三角形,剩下两个点与这条线组成的三角形的面积和就是答案. 按照旋转卡壳的思想不难发现,这两个点都是单调的.所以枚举对角线然后扫n圈就可以了. CODE: #define _CRT_SECURE_NO_DEPRECATE #include <cm

bzoj 1185 旋转卡壳 最小矩形覆盖

题目大意 就是求一个最小矩形覆盖,逆时针输出其上面的点 这里可以看出,那个最小的矩形覆盖必然有一条边经过其中凸包上的两个点,另外三条边必然至少经过其中一个点,而这样的每一个点逆时针走一遍都满足单调性 所以可以利用旋转卡壳的思想找到这样的三个点 以每一条边作为基础,循环n次得到n个这样的矩形,找到其中面积最小的即可 然后自己画画图,作出矩形对应的两条边的单位向量,那么这四个点就非常好求了 1 #include <iostream> 2 #include <cstdio> 3 #inc

1185: [HNOI2007]最小矩形覆盖

1185: [HNOI2007]最小矩形覆盖 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special JudgeSubmit: 1426  Solved: 648[Submit][Status][Discuss] Description Input Output Sample Input Sample Output HINT Source 计算几何 vfleaking提供Spj #include<cstdio> #include<cmat

bzoj1185【HNOI2007】最小矩形覆盖

1185: [HNOI2007]最小矩形覆盖 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special Judge Submit: 1114  Solved: 505 [Submit][Status][Discuss] Description 凸包+旋转卡壳 首先有一个结论:矩形一定有一条边在凸包上,否则我们旋转之后一定会得到一个更小的矩形,脑补一下. 然后枚举凸包上的边,用旋转卡壳维护矩形的另外三条边,同时更新答案即可. #include<ios

【bzoj_1185】[HNOI2007]最小矩形覆盖

咳咳..这会来总结一下1185的解题思路(这么一道破题调了一整天我会乱说? 首先做凸包,这个谁都知道我就不说了 然后问题转化为了凸多边形的最小矩形覆盖 有一个结论是 一个凸包的最小矩形覆盖一定有矩形的一条边在凸包上 请注意是结论..不是猜想.这个结论的正确性嘛..wys神犇给出的解释是这样的 好吧.. 然后我们可以以逆时针的顺序枚举每条边,作为矩形的一条边所在的直线.显然,矩形与这条边平行的边一定过这条边的对踵点.这一步骤旋转卡壳就可以在均摊O(1)的时间下完成. 然后问题来了,如何在快速找到最

BZOJ 1185 [HNOI2007]最小矩形覆盖:凸包 + 旋转卡壳

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1185 题意: 给出二维平面上的n个点,问你将所有点覆盖的最小矩形面积. 题解: 先找出凸包,然后旋转卡壳. 在旋转卡壳中有一个结论:最小覆盖矩形一定有一条边在凸包上. 所以先枚举矩形在凸包上的那条边(p[i],p[i+1]),然后利用单调性找出p[i]的对踵点p[u]. 至于左右两侧的切点p[l]和p[r],要利用它们连线在直线(p[i],p[i+1])上投影长度的单调性求出. 最后将

HDU 5251 矩形面积(二维凸包旋转卡壳最小矩形覆盖问题) --2015百度之星题目

B - 矩形面积 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Description 小度熊有一个桌面,小度熊剪了很多矩形放在桌面上,小度熊想知道能把这些矩形包围起来的面积最小的矩形的面积是多少. Input 第一行一个正整数 T,代表测试数据组数(),接下来 T 组测试数据. 每组测试数据占若干行,第一行一个正整数 ,代表矩形的数量.接下来 N 行,每行 8