1. 程式人生 > >二維凸包求解(Andrew演算法 )

二維凸包求解(Andrew演算法 )

Andrew演算法是Graham演算法的變種。
其主要思想為把凸包上的點依次放入棧中,
如果發現形成了凹多邊形(叉積為負值)
就刪除一些點,使得又能夠維持凸的形態。

這時就會發現,處理各個點需要按照x從左往右的順序,排序即可
當然,這只是處理了下凸的一個凸殼,倒過來再刷一次,就得到了整個凸包

程式碼:

#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <fstream>
#include <string>
#include <sstream>

using namespace std;

struct point {
    int x,y;
}pt[1005];

int sta[1005], ans[1005], cnt;

int cmp(point a, point b)
{
    //左下角優先,先左後下
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}

int cross(point p0, point p1, point p2)
{
    //大於0,逆時針方向
    return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}

//Andrew演算法
void convex(int n)
{
    sort(pt, pt+n, cmp);
    cnt = 0;
    int top = 0;
    sta[top++] = 0;  //先處理 下凸包
    sta[top++] = 1;
    for(int i = 2; i < n; ++i)
    {
        while(top > 1 && cross(pt[sta[top-2]], pt[sta[top-1]], pt[i]) <= 0)
        {
            top--;
        }
        sta[top++] = i;
    }
    for(int i = 0; i < top; ++i)
    {
        ans[cnt++] = sta[i];
    }

    top = 0;             //求上凸包,並將pt[n-1]與pt[0]連線起來
    sta[top++] = n - 1;
    sta[top++] = n - 2;
    for(int i = n - 3; i >= 0; --i)
    {
        while(top > 1 && cross(pt[sta[top-2]], pt[sta[top-1]], pt[i]) <= 0)
        {
            top--;
        }
        sta[top++] = i;
    }
    for(int i = 0; i < top; ++i)
    {
        ans[cnt++] = sta[i];  //兩個凸包的頭結點和尾結點存入兩次
    }
}

int main()
{
    ifstream in("points.txt");
    if(in)
    {
        string line;
        string data1;
        string data2;
        stringstream input;
        int num = 0;
        int sz;
        bool first = true;
        while(getline(in, line))
        {
            if(first)
            {
               sz = stoi(line);
//               cout << "sz = " << sz << endl;
               first = false;
            } else {
                input.clear();
                input.str(line);
                input >> data1 >> data2;
//                cout << "data1 = " << data1 << endl;
                pt[num].x = stoi(data1);
                pt[num].y = stoi(data2);
                ++num;
            }
        }
        //cout << "num = " << num << endl;  // num == 9

        //計算9個點組成的凸包
        convex(9);

        //輸出凸包點的座標
        for(int i = 0; i < cnt; ++i)
        {
            cout << "x = " << pt[ans[i]].x << "  y = " << pt[ans[i]].y << endl;
        }

    } else {
        cout << "no such file" << endl;
    }


    return 0;
}

/************points.txt*******************
9
1 3
2 7
3 1
4 5
5 4
6 9
7 8
8 2
9 6
******************************************/