zoukankan      html  css  js  c++  java
  • 凸包---graham scan算法 + 例题P2742

    ToLeftTest

    这是一个判断一个点在向量的左边还是右边的算法。

    如上图,我们有三个点,假定分别有坐标。

    • (a) ((xa, ya))
    • (b) ((xb, yb))
    • (c) ((xc, yc))

    则有向量

    • (vec A = b - a = (xb - xa, yb - ya))
    • (vec B = c - a = (xc - xa, yc - ya))

    叉乘 (vec A imes B) 也就是 (mid A mid mid B mid sin alpha),我们可以得到:
    如果 (vec A 和 vec B) 夹角是在 ((0, 2pi)) 之间的时候叉乘是正数,否者就是负数,这也就正好对应了 c 点是在 ab 向量的左侧还是右侧。

    graham scan

    graham scan算法的思想

    • 首先我们得找到一个基准点,一般取最下面而且最左边的,也就是 (lowest than leftmost)
    • 以这个点为基准点,在逆时针方向上找到一条最近的极边,通过逆时针扫描,对点进行排序,如果有两个点在同一条直线上的话,取离基准点最近的点序号更前。

    如下图是一个完整的点排序过程

    • 从这里我们不难发现前两个点一定是极点,其连成的边也一定是级边。
    • 接下来的算法就是通过最后两个找到的点取找下一个极点。也就是对这三个点做ToLeftTest。如果满足下一个点在这最后找到的两个点的左边,则暂时先判定这个点是极点。重复操作。

    下面是整个算法的演示过程

    3显然在12的左边,建立级边。

    对2,3,4分析发现,4在2,3的右侧,把已经找好的最后的一个点删去(也就是3),再通过对1, 2, 4检测,ToLeftTest成立,4符合要求。

    后面的点,没有歧义了,最后就会变成这样。

    完美的凸包构造出来了

    回溯选点

    我们可以看出这里已经回溯选点了一次,但是在下一次选点的时候还是不能满足,因此我们再不满足要求的时候需要不断的回溯选点

    代码

    这里选了一道板子题
    P2742 [USACO5.1]圈奶牛Fencing the Cows /【模板】二维凸包

    //Powered by CK
    #include<bits/stdc++.h>
    using namespace std;
    const double INF = 1e100;
    const double pi = acos(-1.0);
    const double eps = 1e-8;
    const int N = 1e5 + 10;
    int n, cnt;
    struct point {
        double x, y;
        point(double a = 0.0, double b = 0.0) : x(a), y(b) {}
    }p[N], ans[N], fir;
    int sgn(double x) {
        if(fabs(x) < eps)   return 0;
        if(x > 0)   return 1;
        return -1;
    }
    point operator -(point a, point b) {
        return point(a.x - b.x, a.y - b.y);
    }
    double cross(point a, point b) {
        return a.x * b.y - a.y * b.x;
    }
    double dis(point a, point b) {
        point temp = a - b;
        return sqrt(temp.x * temp.x + temp.y * temp.y);
    }
    bool cmp(point a, point b) {
        int flag = sgn(cross(a - fir, b - fir));
        if(flag == 1)   return true;
        if(flag == 0 && dis(fir, a) < dis(fir, b))  return true;
        return false;
    }
    void graham() {
        sort(p, p + n, cmp);
        ans[0] = p[0], ans[1] = p[1], cnt = 2;
        for(int i = 2; i < n; i++) {
            while(sgn(cross(ans[cnt - 1] - ans[cnt - 2], p[i] - ans[cnt - 2])) == -1)
                cnt--;
            ans[cnt++] = p[i];
        }
        ans[cnt] = ans[0];
        double target = 0;
        for(int i = 0; i < cnt; i++)
            target += dis(ans[i], ans[i + 1]);
        printf("%.2f
    ", target);
    }
    int main() {
        // freopen("in.txt", "r", stdin);
        fir.x = fir.y = INF;
        scanf("%d", &n);
        for(int i = 0; i < n; i++) {
            scanf("%lf %lf", &p[i].x, &p[i].y);
            if(p[i].y < fir.y)  fir = p[i];
            else if(p[i].y == fir.y && p[i].x < fir.x)  fir = p[i];
        }
        graham();
        return 0;
    }
    
  • 相关阅读:
    PHP 单态设计模式
    五中常见的PHP设计模式
    PHP如何定义类及其成员属性与操作
    thinkphp 中MVC思想
    1.4 算法
    1.3 迭代器
    1.2 容器-container
    1.1 STL 概述
    2.3顺序容器-deque
    2.2 顺序容器-list
  • 原文地址:https://www.cnblogs.com/lifehappy/p/12687893.html
Copyright © 2011-2022 走看看