博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【洛谷 P3187】 [HNOI2007]最小矩形覆盖 (二维凸包,旋转卡壳)
阅读量:5291 次
发布时间:2019-06-14

本文共 3353 字,大约阅读时间需要 11 分钟。

嗯,毒瘤题。
首先有一个结论,就是最小矩形一定有条边和凸包重合。脑补一下就好了。
然后枚举凸包的边,用旋转卡壳维护上顶点、左端点、右端点就好了。
上顶点用叉积,叉积越大三角形面积越大,对应的高也就越大。两边的点用点积,点积越大投影越大。
然后就是精度问题。这种实数计算最好不要直接用比较运算符,要用差和\(eps\)的关系来比较,我就是一直卡在这里。还好有爆炸\(OJ\)离线题库提供的数据。。。

#include 
#include
#include
using namespace std;const int MAXN = 50010;const double eps = 1e-8;struct point{ double x, y; inline double dis(){ return sqrt(x * x + y * y); } inline void print(){ if(fabs(x) < 1e-10) x = 0; if(fabs(y) < 1e-10) y = 0; printf("%.5lf %.5lf\n", x, y); }}p[MAXN];inline double sig(double x){ return (x > eps) - (x < -eps);}int operator == (point a, point b){ return a.x == b.x && a.y == b.y;}point operator * (point a, double b){ // ba return (point){ a.x * b, a.y * b };}double operator * (point a, point b){ // a x b return a.x * b.y - b.x * a.y;}double operator / (point a, point b){ // a . b return a.x * b.x + a.y * b.y;}point operator - (point a, point b){ // a - b return (point){ a.x - b.x, a.y - b.y };}point operator + (point a, point b){ // a + b return (point){ a.x + b.x, a.y + b.y };}int cmp(const point a, const point b){ return a.x == b.x ? a.y < b.y : a.x < b.x;}inline int judge(point a, point b, point c){ //Kab > Kac return (b.y - a.y) * (c.x - a.x) > (c.y - a.y) * (b.x - a.x); }inline double mult(point a, point b, point c){ return (a - c) * (b - c);}inline double calc(point a, point b, point c){ return (b - a) / (c - a);}int n, top, tp;point st[MAXN], ts[MAXN], Ans[5];double ans = 1e18, d, a, b, L, R;int main(){ scanf("%d", &n); for(int i = 1; i <= n; ++i) scanf("%lf%lf", &p[i].x, &p[i].y); sort(p + 1, p + n + 1, cmp); for(int i = 1; i <= n; ++i){ if(p[i] == p[i - 1]) continue; while(top > 1 && judge(st[top - 1], st[top], p[i])) --top; st[++top] = p[i]; } for(int i = 1; i <= n; ++i){ if(p[i] == p[i - 1]) continue; while(tp > 1 && !judge(ts[tp - 1], ts[tp], p[i])) --tp; ts[++tp] = p[i]; } for(int i = tp - 1; i; --i) st[++top] = ts[i]; --top; int j = 2, k = 2, l = 2; for(int i = 1; i <= top; ++i){ while(sig(mult(st[i], st[i + 1], st[j]) - mult(st[i], st[i + 1], st[j + 1])) <= 0) if(++j > top) j = 1; while(sig(calc(st[i], st[i + 1], st[k]) - calc(st[i], st[i + 1], st[k + 1])) <= 0) if(++k > top) k = 1; if(i == 1) l = k; while(sig(calc(st[i], st[i + 1], st[l]) - calc(st[i], st[i + 1], st[l + 1])) >= 0) if(++l > top) l = 1; d = (st[i] - st[i + 1]).dis(); R = calc(st[i], st[i + 1], st[k]) / d; L = calc(st[i], st[i + 1], st[l]) / d; b = fabs(mult(st[i], st[i + 1], st[j]) / d); a = R - L; if(a * b < ans){ ans = a * b; Ans[1] = st[i] + (st[i + 1] - st[i]) * (R / d); Ans[2] = Ans[1] + (st[k] - Ans[1]) * (b / (st[k] - Ans[1]).dis()); Ans[3] = Ans[2] + (st[i] - Ans[1]) * (a / R); Ans[4] = Ans[3] + (Ans[1] - Ans[2]); } } printf("%.5lf\n", ans); double Min = 1e18, pos; for(int i = 1; i <= 4; ++i) if(Ans[i].y < Min) Min = Ans[i].y, pos = i; for(int i = pos; i <= 4; ++i) Ans[i].print(); for(int i = 1; i < pos; ++i) Ans[i].print(); return 0;}

转载于:https://www.cnblogs.com/Qihoo360/p/10321748.html

你可能感兴趣的文章
自定义分页
查看>>
Oracle事务
查看>>
任意输入10个int类型数据,把这10个数据首先按照排序输出,挑出这些数据里面的素数...
查看>>
String类中的equals方法总结(转载)
查看>>
图片问题
查看>>
bash使用规则
查看>>
AVL数
查看>>
第二章练习
查看>>
ajax2.0
查看>>
C#时间截
查看>>
C语言程序设计II—第九周教学
查看>>
C# 获取系统时间及时间格式转换
查看>>
WCF、WebAPI、WCFREST、WebService之间的区别
查看>>
2018-2019-2-20175332-实验四《Android程序设计》实验报告
查看>>
全栈12期的崛起之捡点儿有用的说说
查看>>
基础类型
查看>>
属性动画
查看>>
标识符
查看>>
Swift 常量&变量
查看>>
Sqli labs系列-less-4 这关好坑!!!
查看>>