题意描述:
对任意给定的平面上的点集,求最小凸多边形使得点集中的点要么在凸多边形的边上,要么在凸多边形的内部。
Graham算法描述:
- 在所有的点中找到一点p0,使得p0的纵坐标值最小,在有多个最小纵坐标的情况下,找横坐标最小的那一个。
- 将所有的点< p0, p1,…pn> 按规则(相对于p0的幅角从小到大,也就是绕p0逆时针,如有幅角相等的点,只保留离p0最远的那个,其他的删除)排序。
- 如果所剩的顶点数m小于3,不能构成凸包,retrun false;
- 将前三个点如空栈S。
- 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;
}