凸包—Graham扫描法 && Jarvis March(Gift Wrapping)

Graham扫描法

提要

1.点线位置:利用向量叉积判断点在直线的某一侧
2.极角排序:选定边界上的点(一般为最左下点),按相对该点构成极角的大小排序
3.退化问题:出现多点在同一条直线上时,将距离加入排序


步骤

1.按最左下点进行极角排序
2.选择序列前两个依次入栈A,剩余则从序列末尾开始往前依次入栈B
3.如果栈B非空,每次从栈A中取出两个点 a , b 构成直线ab,从栈B中取出一个点c,判断c与ab的位置关系。如果c在ab左侧,a、b回到栈A,c再入栈A,若果c在ab右侧,a回到栈A中。


求凸包并输出凸包边上的点,这里是顺时针顺序

///Graham Scan
///O(n*log(n))
#include <stack>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 10000 + 5;
const double PI = acos(-1.0);

struct Point{
    double x, y;
    bool extreme;
    int pos;
};
Point P[maxn];

int n;

double dis(Point A, Point B)
{
    return sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y));
}

double cross(Point O, Point A, Point B)
{
    return (A.x - O.x)*(B.y - O.y) - (A.y - O.y)*(B.x - O.x);
}

///B to OA
bool cmp(Point A, Point B)
{
    ///sort base 0
    int cur = 0;
    double temp = cross(P[cur], A, B);

    ///control direction
    if(temp > 0) return true;
    else if(temp == 0 && (dis(P[cur], A) < dis(P[cur], B))) return true;
    else return false;
}

bool ToLeft(Point O, Point A, Point B)
{
    double temp = cross(O, A, B);
    if(temp > 0) return true;
    else if(temp == 0 && (dis(O, A) < dis(O, B))) return true;
    else return false;
}

void Graham()
{
    sort(P+1, P+n, cmp);  ///extreme angle sort

    stack<Point> s1, s2;
    while(!s1.empty()) s1.pop();
    while(!s2.empty()) s2.pop();

    if(n == 1){
        s1.push(P[0]);
        P[0].extreme = true;
    }
    else if(n == 2){
        s1.push(P[0]);
        s1.push(P[1]);
        P[0].extreme = true;
        P[1].extreme = true;
    }
    else{
        s1.push(P[0]), s1.push(P[1]);
        for(int i = n-1; i >=2; i--) s2.push(P[i]);

        while(!s2.empty()){
            Point B = s1.top();s1.pop();
            Point A = s1.top();
            Point C = s2.top();
            B.extreme = false;
            if(ToLeft(A, B, C)){
                s2.pop();
                s1.push(B), s1.push(C);
                B.extreme = true; C.extreme = true;
            }
        }
    }

    Point a[1005];
    int cnta = 0;
    while(!s1.empty()){
        a[cnta++] = s1.top();
        s1.pop();
    }

    for(int i = 0; i < cnta; i++)
        printf("%d ",a[i].pos);
    printf("\n");
}

int main()
{
    while(~scanf("%d", &n)){
        int tmp = 0;
        for(int i = 0; i < n; i++){
            scanf("%lf%lf", &P[i].x, &P[i].y);
            P[i].pos = i;///the number of P[i]
            P[i].extreme = false;
            if(P[i].y < P[tmp].y || (P[i].y == P[tmp].y && P[i].x < P[tmp].x))
                tmp = i;
        }
        swap(P[tmp], P[0]);
        Graham();

    }
    return 0;
}

Jarvis March(Gift Wrapping)

步骤:

1.选出最左下方的点,顺时针扩展成一条极边 K->S
2.遍历其他所有点,寻找一个点 T(T!=K) 使得边 S->T 在所有点的右侧,当下一次选的点是初始点时结束
(由于一次是遍历所有的点,所以选出的凸包上的点是凸包角上的点)
(时间复杂度为O(N*H),最坏的情况是所有点都是凸包的角上的点)

///Jarvis March(Gift Wrapping)
///O(N*H)
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 10000+5;

struct Point{
    double x, y;
    int succ;
    bool extreme;
};

double dis(Point A, Point B)
{
    return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}

//t to k-s
bool ToLeft(Point k, Point s, Point t)
{
    double temp = (s.x - k.x)*(t.y - k.y) - (s.y - k.y)*(t.x - k.x);
    if(temp > 0) return true;
    else if(temp == 0 && dis(k, s) < dis(k, t)) return true;
    else return false;
}

int LTL(Point P[], int n)
{
    int ltl = 0;
    for(int k = 1; k < n; k++)
        if(P[k].y < P[ltl].y ||
           (P[k].y == P[ltl].y &&
            P[k].x < P[ltl].x))
            ltl = k;
    return ltl;
}

void Jarvis(Point P[], int n){
    for(int k = 0; k < n; k++) P[k].extreme = false;
    int ltl = LTL(P, n); int k = ltl;
    do{
        P[k].extreme = true;
        int s = -1;
        for(int t = 0; t < n; t++)
            if(t != k &&
               (s == -1 || ToLeft(P[k], P[s], P[t])))
                s = t;
        P[k].succ = s; k = s;
    }while(ltl != k);
}

int main()
{
    int n;
    Point a[maxn];
    while(~scanf("%d", &n)){
        memset(a, 0, sizeof(a));
        for(int i = 0; i < n; i++){
            scanf("%lf%lf", &a[i].x, &a[i].y);
        }
        Jarvis(a, n);
        for(int i = 0; i < n; i++){
            if(a[i].extreme) printf("%d\n", i);
        }
    }
    return 0;
}
posted @ 2017-07-06 19:19  />.<\  阅读(943)  评论(0编辑  收藏  举报