計算幾何--凸包總結


了解凸包及Graham掃描法

       問題描述:二位平面內,給定n個散亂的點,求一個最小凸多邊形(凸包),使得n個點都不在凸多邊形外。

       問題的解決用到Graham算法:

算法步驟:

  1.取y坐標最小的一點,作為p0,顯然p0一定在凸包上。

 

  2.將p0作為坐標系原點,其他點按極角從小到大排序,從p1開始編號。

 

 

  3.從小到大遍歷所有點:顯然[p0, p1] 在凸包中

 

 

  4.連接p1, p2的時候需要判斷:p0->p1 叉乘 p1->p2 是否大於0:

    > 0 p1->p2 在 p0->p1 夾角小於π,物理意義:p1->p2 在 p0->p1的左邊,滿足凸多邊形定義。

    = 0 p1->p2 與 p0->p1 共線,同向滿足,相反不滿足。

    < 0 p1->p2 在 p0->p1 夾角大於π,不滿足。

 

 

  5.連接p2, p3 向量p2->p3在p1->p2左邊,滿足定義,當連接p3, p4的時候,發現不滿足定義了,此時要放棄p3, 從p2開始回溯,找到第一個滿足要求的點。

 

 

  6.以此類推,知道回到p0點。

 

 

Graham scan 正確性:

       令散點的數量為k,散點(p0 ~ pk – 1)已經按照極角排序。

       當k=3時,顯然,p0->p1時凸包的一條邊,且p2 極角小於p1, 那么p1->p2在p0->p1的左側,所以p1->p2保留。

       當k=n時,假設此時(p0 ~ pn – 1) 都按照Graham scan找出“最完美的凸包”

       當k=n+1時,如果pn – 1 -> pn 在 pn – 2 > pn – 1左邊,如下圖,如果舍棄pn,直接連接pn – 1 -> p0, 那么pn在多邊形外,不滿足要求。

      

 

              證畢。

代碼實現:

/****************************凸包模板*******************************/

const double eps = 1e-8;

int sgn(double x) {
  if (fabs(x) < eps)
    return 0;

  if (x < 0)
    return -1;
  else
    return 1;
}

struct Point {
  double x, y;
  Point() {}
  Point(double _x, double _y) {
    x = _x;
    y = _y;
  }

  Point operator-(const Point &b) const { return Point(x - b.x, y - b.y); }
  //叉積
  double operator^(const Point &b) const { return x * b.y - y * b.x; }
  //點積
  double operator*(const Point &b) const { return x * b.x + y * b.y; }
  void input() {
    scanf("%lf%lf", &x, &y);
  }
};

struct Line {
  Point s, e;
  Line() {}
  Line(Point _s, Point _e) {
    s = _s;
    e = _e;
  }
};

//*兩點間距離
double dist(Point a, Point b) { return sqrt((a - b) * (a - b)); }

/*
 * 求凸包,Graham算法
 * 點的編號0~n-1
 * 返回凸包結果Stack[0~top-1]為凸包的編號
 */

const int MAXN = 1010;
Point List[MAXN];
int Stack[MAXN];  //用來存放凸包的點
int top;  //表示凸包中點的個數

//相對於List[0]的極角排序

bool _cmp(Point p1, Point p2) {
  double tmp = (p1 - List[0]) ^(p2 - List[0]);
  if (sgn(tmp) > 0)
    return true;
  else if (sgn(tmp) == 0 && sgn(dist(p1, List[0]) - dist(p2, List[0])) <= 0)
    return true;
  else
    return false;
}

void Graham(int n) {
  Point p0;
  int k = 0;
  p0 = List[0];
  //找最下邊的一個點
  for (int i = 1; i < n; i++) {
    if ((p0.y > List[i].y) || (p0.y == List[i].y && p0.x > List[i].x)) {
      p0 = List[i];
      k = i;
    }
  }

  swap(List[k], List[0]);
  sort(List + 1, List + n, _cmp);
  if (n == 1) {
    top = 1;
    Stack[0] = 0;
    return;
  }

  if (n == 2) {
    top = 2;
    Stack[0] = 0;
    Stack[1] = 1;
    return;
  }

  Stack[0] = 0;
  Stack[1] = 1;
  top = 2;
  for (int i = 2; i < n; i++) {
    while (top > 1 &&
        sgn((List[Stack[top - 1]] - List[Stack[top - 2]]) ^ (List[i] - List[Stack[top - 2]])) <= 0)
      top--;
    Stack[top++] = i;
  }
}

/****************************凸包模板*******************************/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM