Graham Scan算法

预备知识:凸集:集合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;
}

---------------------

有空补充一下快包和旋转卡壳

猜你喜欢

转载自blog.csdn.net/qq_39942341/article/details/79841996
今日推荐