寻找凸包(Graham扫描法)

寻找凸包(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
while point next-to-top(S), point top(S), point i 不能构成一个左转的三角形
pop(S)
push(point i, S)
return S

栈S中的点即为构成凸包的点。
在这里插入图片描述
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<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#define INF 0x3f3f3f3f
#define MAXN 50
using namespace std;
struct point { char name; double x, y; };
vector<point> res;    //数组模拟栈
vector<point> 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;
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值