计算几何--Graham 凸包扫描法模板(高封装)

2020.6.30
下午不想动脑子了,干脆来学习一下计算几何。果然写数据结构就是爽,不用怎么动脑子就行。这个graham扫描法oi考得其实并不算很多,倒是acm一天到晚考这个东西。去年去p大大概听郭神讲过这个,就是排序完不断寻找叉积大于0(向外拐)的上凸壳,然后同样方法扫一遍下凸包就行,听说会被卡成n方,不过我觉得icpc的命题组还是可以信赖的,学了再说。新队友tg2=,没办法计算几何只能我来抗了。这个算法不是很难,我对一些能优化的地方优化了一下,就是可能这样一来常数有点大了。但是绝对好懂,了解过这个算法大概的步骤的一看我的代码应该就懂了,我是把能封装的东西全部封装了一遍,为了可读性,用了工厂模式和typedef把vector和point公用一个结构体,虽然本身也就是一样,但是这样一来可读性属实增加了不少,看代码吧。就是进栈出栈。

代码:

#include <bits/stdc++.h>
using namespace std;
#define limit (100000 + 5)//防止溢出
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f
#define lowbit(i) i&(-i)//一步两步
#define EPS 1e-6
#define FASTIO  ios::sync_with_stdio(false);cin.tie(0);
#define ff(a) printf("%d\n",a );
#define pi(a,b) pair<a,b>
#define rep(i, a, b) for(ll i = a; i <= b ; ++i)
#define per(i, a, b) for(ll i = b ; i >= a ; --i)
#define mint(a,b,c) min(min(a,b), c)
#define MOD 988244353
#define FOPEN freopen("C:\\Users\\tiany\\CLionProjects\\acm_01\\data.txt", "rt", stdin)
#define FOUT freopen("C:\\Users\\tiany\\CLionProjects\\acm_01\\dabiao.txt", "wt", stdout)
typedef long long ll;
typedef unsigned long long ull;
ll read(){
    ll sign = 1, x = 0;char s = getchar();
    while(s > '9' || s < '0' ){if(s == '-')sign = -1;s = getchar();}
    while(s >= '0' && s <= '9'){x = x * 10 + s - '0';s = getchar();}
    return x * sign;
}//快读
void write(ll x){
    if(x < 0) putchar('-'),x = -x;
    if(x / 10) write(x / 10);
    putchar(x % 10 + '0');
}
struct point{
    double x,y;
    point(double _x = 0.00,double _y = 0.00):x(_x), y(_y){}
    double operator^(const point &rhs)const {
        return x * rhs.y - y * rhs.x;//叉积顺时针大于0,平行等于0,逆时针小于0
    }//叉积
    static point make_vector(point p1, point p2){
        return point(p2.x - p1.x,p2.y - p1.y );
    }
    bool operator<(const point &p2)const{
        return y == p2.y ? x < p2.x : y < p2.y;
    }
}a[limit];
double sq(double i){
    return i * i;
}
double dist(point x , point y){
    return sqrt(sq(x.x - y.x) + sq(x.y - y.y));//求欧几里得距离
}
int n;
point stk[limit];
int top;
typedef point vect;
int main() {
#ifdef LOCAL
    FOPEN;
#endif
    n = read();
    top = 0;
    rep(i ,1, n){
        double x, y;
        scanf("%lf%lf" , &x, &y);
        a[i].x = x, a[i].y = y;
    }
    sort(a + 1, a + 1 + n);
    stk[++top] = a[1] , stk[++top] = a[2];
    rep(i ,3, n){
        vect u = point::make_vector(stk[top - 1] , stk[top]);
        vect v = point::make_vector(stk[top], a[i]);
        while (top > 1 && (u ^ v) < 0){
            --top;
            u = point::make_vector(stk[top - 1] , stk[top]);
            v = point::make_vector(stk[top], a[i]);
        }
        stk[++top] = a[i];
    }//上凸包
    int threshold = top;
    stk[++top] = a[n] , stk[++top] = a[n - 1];
    per(i ,1,n - 2){
        vect u = point::make_vector(stk[top - 1] , stk[top]);
        vect v = point::make_vector(stk[top], a[i]);
        while (top > threshold + 1 && (u ^ v) < 0){
            --top;
            u = point::make_vector(stk[top - 1] , stk[top]);
            v = point::make_vector(stk[top], a[i]);
        }
        stk[++top] = a[i];
    }
    double ans = 0.0;
    rep(i ,1, top - 1){
        ans += dist(stk[i], stk[i + 1]);
    }
    printf("%.2lf" , ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值