寻找凸包(Graham扫描法)

题意描述:

对任意给定的平面上的点集,求最小凸多边形使得点集中的点要么在凸多边形的边上,要么在凸多边形的内部。

Graham算法描述:

  1. 在所有的点中找到一点p0,使得p0的纵坐标值最小,在有多个最小纵坐标的情况下,找横坐标最小的那一个。
  2. 将所有的点< p0, p1,…pn> 按规则(相对于p0的幅角从小到大,也就是绕p0逆时针,如有幅角相等的点,只保留离p0最远的那个,其他的删除)排序。
  3. 如果所剩的顶点数m小于3,不能构成凸包,retrun false;
  4. 将前三个点如空栈S
  5. for i = 3 to m
  6.       while point next-to-top(S), point top(S), point i 不能构成一个左转的三角形
  7.                    pop(S)
  8.       push(point i, S)
  9. return S

栈S中的点即为构成凸包的点。

图解:
寻找凸包(Graham扫描法)_第1张图片

example

输入

8
A -1 2
B 1 1
C 3 4
D 2 2
E 2 5
F 3 2
G 2 6
H 3 3

输出:

B 1 1
F 3 2
C 3 4
G 2 6
A -1 2

#include
#include
#include
#include
#define INF 0x3f3f3f3f
#define MAXN 50
using namespace std;
struct point { char name; double x, y; };
vector res;    //数组模拟栈
vector points; //点集
point p0; //y最小的情况下, x最小的点
double dis(point p1, point p2) {
    return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y));
}
bool cmp(const point p1, const point p2){  //atan2(y, x)返回向量xy和x轴正方向的夹角(-180到180)
    if (atan2(p1.y - p0.y, p1.x - p0.x) == atan2(p2.y - p0.y, p2.x - p0.x))
        return dis(p0, p1) < dis(p0, p2);
    return atan2(p1.y - p0.y, p1.x - p0.x) < atan2(p2.y - p0.y, p2.x - p0.x);
}
bool istureleft(const point p0, const point p1, const point p2) { //p2是否相对于p1左转
    double angle1 = atan2(p1.y - p0.y, p1.x - p0.x);
    double angle2 = atan2(p2.y - p0.y, p2.x - p0.x);
    if (angle1 < 0) angle1 += 2 * 3.1416;
    if (angle2 < 0) angle2 += 2 * 3.1416;
    return angle1 - angle2 < 0;
}
int main() {
    char c;
    double x, y;
    size_t i, n, index;
    while (cin >> n) {
        res.clear(); //清空栈
        p0 = { ' ', INF, INF };        //初始化p0
        points.clear();
        for (i = 0; i < n; ++i) {
            cin >> c >> x >> y;
            points.push_back(point{ c, x, y });
            if (y < p0.y || y == p0.y && x < p0.x) { index = i, p0 = { c, x, y }; } //更新p0
        }
        sort(points.begin(), points.end(), cmp);
        index = 0;
        res.push_back(points[index++]); //前三个点进栈
        res.push_back(points[index++]);
        res.push_back(points[index++]);
        for (; index < n; index++) {//对后边的每一个点
            while (!istureleft(*(res.end() - 2), *(res.end() - 1), points[index]))
                res.erase(res.end() - 1);
            res.push_back(points[index]);
        }
        for (i = 0; i < res.size(); ++i)
            cout << res[i].name << " " << res[i].x << " " << res[i].y << endl;
    }
    return 0;
}

你可能感兴趣的:(算法,算法)