• 计算几何学习笔记


    点、向量

    这部分没啥好讲的。点可以使用坐标表示,向量可以使用点表示。

    这里记录一些我知道的基础知识。

    向量长度

    对于向量(A(x,y)),其长度记为(|A|=sqrt{x^2+y^2})

    向量叉积

    对于两个向量 (A(x_1,y_1),B(x_2,y_2)) 他们的叉积是(A imes B=x_1cdot y_2-x_2cdot y_1)。叉积的绝对值还等于以这两个向量为邻边的平行四边形面积,即(|A|cdot|B|cdotsin heta),其中( heta)是他们的夹角。叉积的正负号由右手螺旋定则确定。

    向量点积

    对于两个向量 (A(x_1,y_1),B(x_2,y_2)) 他们的点积是 (Acdot B=x_1cdot y_1+x_2cdot y_2=|A|cdot|B|cdotcos heta),其中( heta)是他们的夹角。

    凸包

    首先将所有点按照 (x) 轴为第一关键字,(y) 轴为第二关键字排序,用单调栈维护凸包即可。判断是否是凸的可以使用叉积判断。

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    const int N = 1e5 + 5;
    const double eps = 1e-10;
    bool eql(double x, double y) { return abs(x - y) < eps; }
    struct Dot {
        double x, y;
        Dot(double _x = 0, double _y = 0) : x(_x), y(_y) {}
        Dot operator-(const Dot &d) const { return Dot(x - d.x, y - d.y); }
        double operator^(const Dot &d) const { return x * d.y - d.x * y; }
        bool operator<(const Dot &d) const { return x < d.x; }
    } dt[N];
    double dis(Dot x, Dot y) { return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y)); }
    int n, stk[N], tp;
    bool vis[N];
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) scanf("%lf%lf", &dt[i].x, &dt[i].y);
        sort(dt + 1, dt + n + 1);
        //先计算上凸壳 
        for (int i = 1; i <= n; ++i) {
        	while (tp >= 2 && ((dt[stk[tp - 1]] - dt[stk[tp]]) ^ (dt[stk[tp]] - dt[i])) > -eps)
        		vis[stk[tp--]] = 0;
        	stk[++tp] = i;
        	vis[i] = 1;
    	}
    	vis[1] = 0;
    	//在围下凸壳的时候需要用 1号点再弹出一些点 
    	int nb = tp;
    	for (int i = n; i >= 1; --i) {
    		if (vis[i])
    			continue ;
    		while (tp > nb && ((dt[stk[tp - 1]] - dt[stk[tp]]) ^ (dt[stk[tp]] - dt[i])) > -eps)
    			--tp;
    		stk[++tp] = i;
    	}
    	double ans = 0;
    	for (int i = 1; i <= tp; ++i)
    		ans += dis(dt[stk[i]], dt[stk[i % tp + 1]]);
    	printf("%.2lf
    ", ans);
        return 0;
    }
    

    旋转卡壳

    模板

    求出凸包。每一条边找到一个点使得这个点距离这条边最远,这条边的两个端点和那个找到的点更新答案。至于如何找到那个点,我们发现凸包上的点和这个线段的距离是单峰的(峰是最值),所以考虑使用双指针的方式解决问题。

    注:一个点和其他点的距离并不单调。因为有可能出现这种情况:

    你发现中间这个线非常短,而两边的特别长。

    另外还需要特判只有两个点的情况,否则会死循环。

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    const int N = 5e4 + 5;
    struct dot {
    	int x, y;
    	dot(int _x = 0, int _y = 0) : x(_x), y(_y) {}
    	dot operator-(const dot &d) const { return dot(x - d.x, y - d.y); }
    	int operator*(const dot &d) const { return x * d.y - y * d.x; }
    	bool operator<(const dot &d) const { return (x == d.x) ? (y < d.y) : (x < d.x); }
    } dt[N];
    int dis(dot a, dot b) { return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); }
    int n, stk[N];
    bool use[N];
    int main() {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; ++i)
    		scanf("%d%d", &dt[i].x, &dt[i].y);
    	sort(dt + 1, dt + n + 1);
    	int tp = 0;
    	for (int i = 1; i <= n; ++i) {
    		while (tp >= 2 && (dt[stk[tp - 1]] - dt[stk[tp]]) * (dt[stk[tp]] - dt[i]) >= 0) {
    			use[stk[tp]] = 0;
    			--tp;
    		}
    		use[i] = 1;
    		stk[++tp] = i;
    	}
    	use[1] = 0;
    	int rec = tp;
    	for (int i = n; i >= 1; --i) {
    		if (use[i]) continue ;
    		while (tp > rec && (dt[stk[tp - 1]] - dt[stk[tp]]) * (dt[stk[tp]] - dt[i]) >= 0)
    			--tp; //这里是>=0,为了避免凸包上出现三点共线。
    		stk[++tp] = i;
    	}
    	if (tp == 3) {
    		printf("%d
    ", dis(dt[stk[1]], dt[stk[2]]));
    		return 0;
    	}
    	int ans = 0, pt = 1;
    	for (int i = 1; i < tp; ++i) {
    		int x = stk[i], y = stk[i + 1];
    		while ((dt[y] - dt[stk[pt]]) * (dt[x] - dt[stk[pt]]) <= (dt[y] - dt[stk[pt % tp + 1]]) * (dt[x] - dt[stk[pt % tp + 1]])) //必须是<=。
    			pt = pt % (tp - 1) + 1;
    		ans = max(ans, max(dis(dt[x], dt[stk[pt]]), dis(dt[y], dt[stk[pt]])));
    	}
    	printf("%d
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    正确理解原型和原型链
    git push之后服务器如何自动更新?
    项目工具三:Swagger导出离线版HTML5和PDF格式api文档
    项目工具二:通过SQL导出单表的结构文档
    项目工具一:把文件夹里所有子文件夹与文件按树形结构导出
    一个完整的HTTP请求过程详细
    SAAS多租户数据逻辑隔离
    JS同步执行代码
    提升JAVA代码的好“味道”
    Connection keepalive
  • 原文地址:https://www.cnblogs.com/skiceanAKacniu/p/13224546.html
Copyright © 2020-2023  润新知