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

来源:互联网 发布:澳洲留学体验 知乎 编辑:程序博客网 时间:2024/05/29 04:37

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


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

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


CODE:

#define _CRT_SECURE_NO_WARNINGS#include <cmath>#include <cstdio>#include <cstring>#include <iomanip>#include <iostream>#include <algorithm>#define MAX 50010using 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;}


0 0
原创粉丝点击