博客
关于我
半平面交学习笔记
阅读量:438 次
发布时间:2019-03-06

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

定义

一条直线将平面分为两部分,其中一半的平面称为半平面。多个半平面的交集被称为半平面交。这个交集可能是凸多边形、无限延伸的平面、直线、线段或单个点等。一般来说,我们规定直线的左侧为所需的半平面,以便区分左右两侧。直线应具有方向性,以便区分左右。

算法流程

  • 将所有直线按照极角从小到大排序,若极角相同则保留左侧的直线。
  • 使用一个双端队列存储当前半平面交,每次通过判断队首与队尾的第一个交点是否满足当前直线来更新。
  • 先用队尾判定队首交点是否合法,再用队首判断队尾交点是否合法。
  • 最终得到的半平面交通常是一个凸多边形。
  • 一定要先弹队尾再弹队首。
  • 代码

    #include 
    #include
    #include
    #include
    #include
    #include
    #include
    using namespace std;#define rg registerinline int read() { rg int x = 0, fh = 1; rg char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') fh = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = (x < 1) ? (x < 3 ? x : x + 3) : x * 10 + (ch - '0'); ch = getchar(); } return x * fh;}const int maxn = 3e5 + 5;long double eps = 1e-16, INF = 1e12;int dcmp(long double now) { return (std::fabs(now) <= eps) ? 0 : (now > 0 ? 1 : -1);}struct Node { long double x, y; Node() {} Node(long double aa, long double bb) : x(aa), y(bb) {} friend Node operator - (const Node& A, const Node& B) { return Node(A.x - B.x, A.y - B.y); } friend Node operator + (const Node& A, const Node& B) { return Node(A.x + B.x, A.y + B.y); } friend Node operator * (const Node& A, long double B) { return Node(A.x * B, A.y * B); } friend Node operator / (const Node& A, long double B) { return Node(A.x / B, A.y / B); } friend long double operator * (const Node& A, const Node& B) { return A.x * B.x + A.y * B.y; } friend long double operator ^ (const Node& A, const Node& B) { return A.x * B.y - A.y * B.x; } friend bool operator == (const Node& A, const Node& B) { return dcmp(A.x - B.x) == 0 && dcmp(A.y - B.y) == 0; } long double angle() { return atan2(y, x); }};long double eps = 1e-12;int main() { // 读取输入并处理 // ... // 算法实现 // ... return 0;}

    习题

    一、P3222 [HNOI2012] 射箭

    设抛物线的解析式为 (y = ax^2 + bx)。对于每一条线段,都必须满足:[ y_{i1} \leq ax_i^2 + bx_i \leq y_{i2} ]即:[ ax_i^2 + bx_i - y_{i1} \geq 0 ][ ax_i^2 + bx_i - y_{i2} \leq 0 ]将 (a, b) 分别看作 (x, y) 就是一个半平面的形式。通过二分法确定第几关后,判断是否存在半平面交即可。

    代码

    #include 
    #include
    #include
    #include
    #include
    #include
    #include
    using namespace std;#define rg registerinline int read() { rg int x = 0, fh = 1; rg char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') fh = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = (x < 1) ? (x < 3 ? x : x + 3) : x * 10 + (ch - '0'); ch = getchar(); } return x * fh;}const int maxn = 3e5 + 5;const long double eps = 1e-16;const long double INF = 1e12;int dcmp(long double now) { return (std::fabs(now) <= eps) ? 0 : (now > 0 ? 1 : -1);}struct Node { long double x, y; Node() {} Node(long double aa, long double bb) : x(aa), y(bb) {} friend Node operator - (const Node& A, const Node& B) { return Node(A.x - B.x, A.y - B.y); } friend Node operator + (const Node& A, const Node& B) { return Node(A.x + B.x, A.y + B.y); } friend Node operator * (const Node& A, long double B) { return Node(A.x * B, A.y * B); } friend Node operator / (const Node& A, long double B) { return Node(A.x / B, A.y / B); } friend long double operator * (const Node& A, const Node& B) { return A.x * B.x + A.y * B.y; } friend long double operator ^ (const Node& A, const Node& B) { return A.x * B.y - A.y * B.x; } friend bool operator == (const Node& A, const Node& B) { return dcmp(A.x - B.x) == 0 && dcmp(A.y - B.y) == 0; } long double angle() { return atan2(y, x); }};long double eps = 1e-12;int main() { int n = read(); if (n <= 10) { long double eps = 1e-14; rg int aa, bb, cc; for (rg int i = 1; i <= n; ++i) { aa = read(); bb = read(); cc = read(); // 生成直线 struct Line { Node s, t; int id; long double ang; Line(Node A = Node(), Node B = Node(), int C = 0) : s(A), t(B), ang((B - A).angle()), id(C) {} friend bool operator < (const Line& A, const Line& B) { long double r = A.ang - B.ang; if (dcmp(r) != 0) return dcmp(r) == -1; return dcmp((A.t - A.s) ^ (B.t - A.s)) == -1; } friend long double operator * (const Line& A, const Line& B) { return (A.t - A.s) ^ (B.t - B.s); } }; sta[++tp] = Line(Node(-INF, INF), Node(-INF, eps), 0); // 生成交点 // ... } // 进行二分法确定 // ... printf("%d\n", r); return 0; } return 0;}

    二、Saber VS Lancer

    设三个项目路程的长度分别为 (a, b, c)。如果 (i) 想要获胜,必须满足对于任意的 (j) 都有:[ \frac{a}{v_i} + \frac{b}{u_i} + \frac{c}{w_i} < \frac{a}{v_j} + \frac{b}{u_j} + \frac{c}{w_j} ]这里涉及三个未知数 (a, b, c),处理起来较为复杂。但如果 (a, b, c) 满足特定条件,(2a, 2b, 2c) 也会满足条件。因此,可以设 (c = 1 - a - b),从而得到一个半平面交的形式。

    此外,还需满足 (x > 0, y > 0, x + y < 1),并处理 (j) 三个项目速度的特殊情况。为了防止精度问题,系数乘以一个基数 (bas)。

    代码

    #include 
    #include
    #include
    #include
    #include
    #include
    #include
    using namespace std;#define rg registerinline int read() { rg int x = 0, fh = 1; rg char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') fh = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = (x < 1) ? (x < 3 ? x : x + 3) : x * 10 + (ch - '0'); ch = getchar(); } return x * fh;}const int maxn = 205;const long double eps = 1e-12;const long double INF = 1e10;const long double bas = 1e5;int dcmp(long double now) { return (std::fabs(now) <= eps) ? 0 : (now > 0 ? 1 : -1);}struct Node { long double x, y; Node() {} Node(long double aa, long double bb) : x(aa), y(bb) {} friend Node operator - (const Node& A, const Node& B) { return Node(A.x - B.x, A.y - B.y); } friend Node operator + (const Node& A, const Node& B) { return Node(A.x + B.x, A.y + B.y); } friend Node operator * (const Node& A, long double B) { return Node(A.x * B, A.y * B); } friend Node operator / (const Node& A, long double B) { return Node(A.x / B, A.y / B); } friend long double operator * (const Node& A, const Node& B) { return A.x * B.x + A.y * B.y; } friend long double operator ^ (const Node& A, const Node& B) { return A.x * B.y - A.y * B.x; } friend bool operator == (const Node& A, const Node& B) { return dcmp(A.x - B.x) == 0 && dcmp(A.y - B.y) == 0; } long double angle() { return atan2(y, x); }};long double eps = 1e-12;int main() { int n = read(); for (rg int i = 1; i <= n; ++i) { // 读取输入并生成直线 // ... } // 进行半平面交计算 // ... if (jud) { if (SI() == 0) jud = 0; } if (jud) { printf("Yes\n"); } else { printf("No\n"); } return 0;}

    三、P3297 [SDOI2013] 逃考

    对于两个亲戚,它们的监视范围分界线是他们两点之间的中垂线。如果跨过这条线,则会被多个亲戚监视。因此,我们可以在边界相邻的亲戚之间建立权重为 1 的边,然后运行最短路径算法。起点是最初监视小杨的亲戚。关键在于如何确定哪两个亲戚的边界是相邻的。

    由于 (n) 的范围较小,可以对每个亲戚运行半平面交算法,求出他们与其他亲戚连线的中垂线,并标记它们所属的亲戚ID。将所有中垂线一起运行半平面交,最后与剩下的直线表示的亲戚连边即可。

    转载地址:http://rwvyz.baihongyu.com/

    你可能感兴趣的文章
    MySQL 证明为什么用limit时,offset很大会影响性能
    查看>>
    Mysql 语句操作索引SQL语句
    查看>>
    MySQL 误操作后数据恢复(update,delete忘加where条件)
    查看>>
    MySQL 调优/优化的 101 个建议!
    查看>>
    mysql 转义字符用法_MySql 转义字符的使用说明
    查看>>
    mysql 输入密码秒退
    查看>>
    mysql 递归查找父节点_MySQL递归查询树状表的子节点、父节点具体实现
    查看>>
    mysql 通过查看mysql 配置参数、状态来优化你的mysql
    查看>>
    mysql 里对root及普通用户赋权及更改密码的一些命令
    查看>>
    Mysql 重置自增列的开始序号
    查看>>
    mysql 锁机制 mvcc_Mysql性能优化-事务、锁和MVCC
    查看>>
    MySQL 错误
    查看>>
    mysql 随机数 rand使用
    查看>>
    MySQL 面试题汇总
    查看>>
    MySQL 面试,必须掌握的 8 大核心点
    查看>>
    MySQL 高可用性之keepalived+mysql双主
    查看>>
    MySQL 高性能优化规范建议
    查看>>
    mysql 默认事务隔离级别下锁分析
    查看>>
    Mysql--逻辑架构
    查看>>
    MySql-2019-4-21-复习
    查看>>