转自https://www.cnblogs.com/wuwangchuxin0924/p/6223152.html
了解凸包及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;
}
}
/****************************凸包模板*******************************/
