预备知识:凸集:集合S中任意两点的连线都在集合S中,如果简单的理解,可以理解为凸边形
凸包:对于给定集合X,所有包含X的凸集的交集,简单的理解,就是包含X的最小凸集,或者就是最外圈的点连起来
用叉乘判断方向:向量a叉乘向量b的值就是|a| |b| sinθ,θ是向量a逆时针旋转到向量b的角度(右手定则),所以叉积是正的就是逆时针旋转过去,等于0就是共线,小于零就是逆时针旋转过去的
Graham Scan算法是一个用来求凸包的算法
第一步:把点按逆时针顺序排好
具体做法:找到左下角的点(横坐标和纵坐标都是最小的),以这个点为极点,想象有一个平行于x轴的并且和x轴正方向同向的极轴,然后进行极角排序
第二步:找凸包
具体做法:开一个栈,top=元素个数,从第0个点遍历到第n个再到第0个
对于第i个点,构造向量a(top-2->top-1),向量b(top-2->i),计算向量a 叉乘 向量b,
如果不足两个点,i入栈
如果=0,说明共线,可以i入栈
如果>0,说明是逆时针的,可以i入栈
如果是<0说明已经凹进去了,第top-1点不符合凸包的定义,top-1出栈,--top,重复执行,直到成功入栈
结合图理解就是如果连2,3的话就已经凹进去了(左上角的)
下面看一个维基的动态版本
代码
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
class point {
public:
double x, y;
point(const double &x = 0, const double &y = 0) :x(x), y(y) {}
};
int n;
//p是点集,convex是栈
point p[1005], convex[1005];
//叉乘
double cross(const point &a, const point &b, const point &c, const point &d) {
return (b.x - a.x)*(d.y - c.y) - (b.y - a.y)*(d.x - c.x);
}
//极角排序,角度相同按x升序
bool cmp(const point &a, const point &b) {
double temp = cross(p[0], a, p[0], b);
if (!temp)return a.x < b.x;
return temp > 0;
}
//距离
double getDistance(const point &a, const point &b) {
return sqrt(1.0*(a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}
//graham scan算法,返回凸包顶点个数
int graham() {
int top = 0, i = 0;
while (i <= n) {
//不足两个直接入栈,否则叉积判断方向,直到成功入栈
while (top >= 2 && cross(convex[top - 2], convex[top - 1], convex[top - 2], p[i%n]) < 0)--top;
convex[top] = p[i%n];
++top;
++i;
}
return top;
}
int main() {
int num = 0;
scanf("%d", &n);
//读入点找到左下角的点
for (int i = 0; i < n; ++i) {
scanf("%lf%lf", &p[i].x, &p[i].y);
if (p[i].y < p[num].y || p[i].y == p[num].y&&p[i].x < p[num].x)num = i;
}
swap(p[0], p[num]);
//极角排序
sort(p + 1, p + n, cmp);
num = graham();
double ans = 0;
//求周长
for (int i = 1; i < num; ++i)
ans += getDistance(convex[i], convex[i - 1]);
printf("%.0lf\n", ans);
return 0;
}
-----------------------
题目:
poj1113
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
class point {
public:
int x, y;
point(const int &x=0, const int &y=0) :x(x), y(y) {}
};
int n;
point p[1005], convex[1005];
int cross(const point &a, const point &b, const point &c, const point &d) {
return (b.x - a.x)*(d.y - c.y) - (b.y - a.y)*(d.x - c.x);
}
bool cmp(const point &a, const point &b) {
int temp = cross(p[0], a, p[0], b);
if (!temp)return a.x < b.x;
return temp > 0;
}
double getDistance(const point &a, const point &b) {
return sqrt(1.0*(a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}
int graham() {
int top = 2, i = 2;
convex[0] = p[0];
convex[1] = p[1];
while (i <= n) {
while (top >= 2 && cross(convex[top - 2], convex[top - 1], convex[top - 2], p[i%n]) < 0)--top;
convex[top] = p[i%n];
++top;
++i;
}
return top;
}
int main() {
int r, num = 0;
scanf("%d%d", &n, &r);
for (int i = 0; i < n; ++i) {
scanf("%d%d", &p[i].x, &p[i].y);
if (p[i].y < p[num].y || p[i].y == p[num].y&&p[i].x < p[num].x)num = i;
}
swap(p[0], p[num]);
sort(p + 1, p + n, cmp);
num = graham();
double ans = 0;
for (int i = 1; i < num; ++i)
ans += getDistance(convex[i], convex[i - 1]);
ans += 2 * acos(-1.0)*r;
printf("%.0lf\n", ans);
return 0;
}
---------------------
有空补充一下快包和旋转卡壳