学习笔记
Andrew算法
算法做两次扫描,先从最左边的点沿下凸包扫描到最右边,再从最右边的点沿上凸包扫描到最左边,上下合在一起是完整的凸包。
例题
模板题:求凸包周长
以下convex_hull
函数为核心求解凸包的函数,凸包顶点放在ch
数组中,返回值为顶点数。
//计算几何
int sgn(db x) {
//判断浮点数是否为0,为0时返回0
if (fabs(x) < eps) return 0;
return x < 0 ? -1 : 1;
}
struct point{
db x, y;
point operator + (point B) {
return point{
x + B.x, y + B.y};}
point operator - (point B) {
return point{
x - B.x, y - B.y};}
bool operator == (point B) {
return sgn(x - B.x) == 0 && sgn(y - B.y) == 0;}
bool operator < (point B) {
//用于sort排序
return sgn(x - B.x) < 0 || (sgn(x - B.x) == 0 && sgn(y - B.y) < 0);
}
}p[maxn], ch[maxn];
db cross(point A, point B) {
return A.x * B.y - A.y * B.x;}//求×积
db distance(point A, point B) {
return hypot(A.x - B.x, A.y - B.y);}
//求凸包,凸包顶点放在ch中,返回值为顶点数
int convex_hull(point *p, int n, point *ch) {
sort(p, p + n); //排序
n = unique(p, p + n) - p; //清除重复元素
int v = 0;
//求下凸包
_for(i, 0, n) {
while (v > 1 && sgn(cross(ch[v - 1] - ch[v - 2], p[i] - ch[v - 2])) <= 0)
v--;
ch[v++] = p[i];
}
int j = v;
//求上凸包
for(int i = n - 2; i >= 0; --i) {
//最右边的已经选中了,i从n-2开始
while (v > j && sgn(cross(ch[v - 1] - ch[v - 2], p[i] - ch[v - 2])) <= 0) {
v--;
}
ch[v++] = p[i];
}
v = (n > 1) ? v - 1 : v; //若n>1,最左边的多加了一次
return v;
}
以下部分求解周长,其中有一些小技巧:
1.求周长,到凸包上最后一个顶点与首个顶点相加的处理:取余。
distance(ch[i], ch[(i + 1)%v])
2.特判一下n
的个数。
_for(i, 0, n) {
scanf("%lf%lf", &a, &b);
p[i] = point{
a, b};
}
int v = convex_hull(p, n, ch); //凸包顶点数
db l = 0.0;
if (n == 1) l = 0;
else if (n == 2) l = distance(ch[0], ch[1]);
else {
_for(i, 0, v) l += distance(ch[i], ch[(i + 1)%v]); //巧妙求周长
}
printf("%.2f\n", l);
上凸包+共线处理Interstellar Travel
解题思路
求顺时针上凸包面积最大。
注意事项
1.一开始对所有点进行排序,x
相同时,y
大的排在前面。
扫描二维码关注公众号,回复:
12422190 查看本文章

2.注意三点共线的情况,判断是否能使得字典序更小。
参考代码
#include<bits/stdc++.h>
using namespace std;
#define LOCAL //提交的时候一定注释
#define _for(i, a, b) for(int i = (a); i < (b); ++i)
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define pb push_back
#define VI vector<int>
#define INF 0x3f3f3f3f
#define mp make_pair
#define lowbit(x) ((x) & (-x))
typedef long long LL;
typedef double db;
const db eps = 1e-8; //定义浮点数误差
const int MOD = 998244353;
const int maxn = 1e6 + 10;
int readint(){
int x; scanf("%d", &x); return x;
}
//计算几何
int sgn(db x) {
//判断浮点数是否为0,为0时返回0
if (fabs(x) < eps) return 0;
return x < 0 ? -1 : 1;
}
struct point{
db x, y;
int pos;
point operator + (point B) {
return point{
x + B.x, y + B.y};}
point operator - (point B) {
return point{
x - B.x, y - B.y};}
bool operator == (point B) {
return sgn(x - B.x) == 0 && sgn(y - B.y) == 0;}
bool operator < (point B) {
//用于sort排序
if (x == B.x) {
if (y == B.y) return pos < B.pos;
else return y > B.y;
} else return x < B.x;
}
}p[maxn], ch[maxn];
db cross(point A, point B) {
return A.x * B.y - A.y * B.x;}//求×积
db distance(point A, point B) {
return hypot(A.x - B.x, A.y - B.y);}
bool judge(point A, point B, point C) {
//判断需不需要回退
point f = B - A, s = C - A;
if (cross(f, s) > 0) return true;
else if (cross(f, s) == 0) {
return C.pos < B.pos;
}
else return false;
}
//求凸包,凸包顶点放在ch中,返回值为顶点数
int convex_hull(point *p, int n, point *ch) {
sort(p, p + n); //排序
//n = unique(p, p + n) - p; //清除重复元素
int v = 0;
//求上凸包
for(int i = 0; i < n; ++i) {
if (i && p[i].x == p[i - 1].x) continue;
while (v > 1 && sgn(judge(ch[v - 2], ch[v - 1], p[i])) > 0) {
v--;
}
ch[v++] = p[i];
}
//v = (n > 1) ? v - 1 : v;
return v;
}
int main() {
#ifdef LOCAL
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
int n, pos; db a, b;
int t;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
_for(i, 0, n) {
scanf("%lf%lf", &a, &b);
p[i] = point{
a, b, i + 1};
}
int v = convex_hull(p, n, ch); //凸包顶点数
_for(i, 0, v) {
if (i) printf(" ");
printf("%d", ch[i].pos);
}
printf("\n");
}
return 0;
}
拓展:数学+凸包brz的序列
巧妙地运用凸包的一道题。
参考资料
算法竞赛从入门到进阶
(真的巨巨巨巨好的一本书!!!)