zoukankan      html  css  js  c++  java
  • OI计算几何 简单学习笔记

    学习平面几何,首先我们要会熟练地应用向量,其次也要知道一些基本的几何知识。(其实看看数学课本就可以了吧)

    因为是看的蓝书,所以很多东西做了引用。(update:还参考了赵和旭dalao的讲义)

    下面先介绍一些常用的操作:

    点的定义

    struct point
    {
    	double x,y;
    	point(double x=0,double y=0):x(x),y(y){}
    };
    

    向量的定义

    typedef point vec
    

    值的判断

    inline int dcmp(double x)
    {	
    	if(fabs(x)<eps) return 0;
    	else if(x<0.0) return -1;
    	else return 1;
    }
    

    向量的基本运算

    vec operator + (vec a,vec b){return vec(a.x+b.x,a.y+b.y);}
    vec operator - (vec a,vec b){return vec(a.x-b.x,a.y-b.y);}
    vec operator * (vec a,double b){return vec(a.x*b,a.y*b);}
    vec operator / (vec a,double b){return vec(a.x/b,a.y/b);}
    

    向量的点积

    这个学过高中数学必修四的应该都知道。
    比如说对于(vec a)(vec b),集合上的定义为:(vec a imes vec b imes cos heta) ,代数的计算方法则为(ax*bx+ay*by)

    inline double dot(vec a,vec b){return a.x*b.x+a.y*b.y;} 
    

    向量的长度

    inline double length(vec a){return sqrt(dot(a,a));}
    

    向量的夹角(返回值为弧度制)

    inline double angle(vec a,vec b){return acos(dot(a,b)/length(a)/length(b));}
    

    向量的叉积

    对于(vec a)(vec b),集合上的定义为:(vec a imes vec b imes sin heta) ,代数的计算方法则为(ax*by-ay*bx)
    它的几何意义表示这两个向量形成的平行四边形的面积。

    inline double cross(vec a,vec b){return a.x*b.y-a.y*b.x;} 
    

    因为叉积在数值上可以表示向量a,b围成的平行四边形的面积,所以我们可以通过叉积来计算三个点围成的三角形的面积——

    inline double area(point a,point b,point c){return cross(b-a,c-a);}
    

    除此之外还有一些应用:

    向量的旋转(注意是逆时针旋转)

    公式为(x_{new}=xcosa-ysina),(y_{new}=xsina+ycosa),其中a为逆时针旋转的角的弧度值。

    inline vec rotate(vec x,double a)
    {return vec(x.x*cos(a)-x.y*sin(a),x.x*sin(a)+x.y*cos(a));}
    

    直线的交点

    inline point getline_intersection(point p,vec v,point q,vec w)
    {
    	vec u=p-q;
    	double t=cross(w,u)/cross(v,w);
    	return p+mul(v,t);
    }
    

    点到直线的距离

    其实数学上有个公式叫做(frac{fabs(ax+by+c)}{sqrt {k^2+1}}),但是这个需要直线的表达式,我们可以利用叉积来更方便地进行计算。( 也就是用平行四边形的面积除以底)

    inline double distance(point p,point a,point b)//a,b组成的是边
    {
    	vec v1=b-a,v2=p-a;
    	return fabs(cross(v1,v2))/length(v1);//如果不取绝对值,得到的是有向距离
    }
    

    多边形的计算(同时适用于凸多边形,凹多边形)

    其实就是从一个顶点开始,将多边形划分成若干个三角形进行计算。(从p0开始划分)

    inline double Area(point* p,int n)
    {
    	double cur_area=0
    	for(int i=1;i<n-1;i++)
    		cur_area+=cross(p[i]-p[0],p[i+1]-p[0]);
    	return cur_area/2;
    }
    

    线段相交判定

    inline bool whether_intersection(point a1,point a2,point b1,point b2)
    {
    	double c1=cross(a2-a1,b1-a1);
    	double c2=cross(a2-a1,b2-a1);
    	double c3=cross(b2-b1,a2-b1);
    	double c4=cross(b2-b1,a1-b1);
    	return (dcmp(c1)*dcmp(c2)<0)&&(dcmp(c3)*dcmp(c4)<0);
    }
    

    多边形的重心

    把一个多边形剖分成很多的三角形,依次给每个三角形求重心,按照面积加权求平均就是该多边形的重心。

    判断一个点是否在多边形内

    从该点随便引(一般都是竖直吧qwq)一条射线,计算它与多边形所有边的交点的数目,如果是奇数个就在多边形内,如果是偶数个就在多边形外。(在线段的两端点上相交不算有交点)
    但是要注意一下细节

    求两圆的交点

    求两圆的内(外)公切线

    如果一条直线和两个圆都相切,这条直线叫做两个圆的公切线。如果两圆在公切线的同侧,称这条公切线为两圆的外公切线,如果两圆分别在公切线的两侧,称这条公切线为两圆的内公切线。

    求凸包

    懒得省事了,直接放一个模板好了qwqwq

    inline bool cmp(struct Node a,struct Node b)
    {
        double A=atan2((a.y-t[1].y),(a.x-t[1].x));
        double B=atan2((b.y-t[1].y),(b.x-t[1].x));
        if(A!=B) return A<B;
        else return a.x<b.x;
    }
    inline double cross(Node a,Node b,Node c){return (a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x);}
    inline void solve()
    {
        t[0]=(Node){0x3f3f3f3f,0x3f3f3f3f};
        int k=0;
        for(int i=1;i<=n;i++)
            if(t[i].y<t[0].y||(t[i].y==t[0].y&&t[i].x<t[0].x))
                t[0]=t[i],k=i;
        swap(t[1],t[k]);
        sort(&t[2],&t[1+n],cmp);
        s[0]=t[1],s[1]=t[2];
        top=1;
        for(int i=3;i<=n;i++)
        {
            while(top&&cross(s[top-1],t[i],s[top])>=0.0) top--;
            s[++top]=t[i];
        }
    }
    

    动态凸包:(请看我的这篇博客

    关键代码:(我这里使用的是极角排序)

    inline void solve(Node x)
    {
        nxt=st.lower_bound(x);
        if(nxt==st.end()) nxt=st.begin();
        pre=get_pre(nxt);
        if(cross((*nxt)-(*pre),x-(*pre))>=0) return;
        cur_ans-=get_len((*nxt)-(*pre));
        cur_ans+=get_len(x-(*nxt))+get_len(x-(*pre)); 
        st.insert(x);
        tmp=get_pre(pre);
        while(cross(x-(*pre),(*pre)-(*tmp))>=0)
        {
            cur_ans-=get_len(x-(*pre))+get_len((*pre)-(*tmp));
            cur_ans+=get_len(x-(*tmp));
            st.erase(pre);
            pre=tmp;
            tmp=get_pre(pre);
        }
        tmp=get_nxt(nxt);
        while(cross(x-(*nxt),(*nxt)-(*tmp))<=0)
        {
            cur_ans-=get_len(x-(*nxt))+get_len((*nxt)-(*tmp));
            cur_ans+=get_len(x-(*tmp));
            st.erase(nxt);
            nxt=tmp;
            tmp=get_nxt(nxt);
        }
        return;
    }
    

    点在多边形内的判定

    method1 射线法

    就是从某一个判定点出发,任意引出一条射线。如果和边界相交奇数次,说明点在多边形内。如果相交偶数次,说明点在多边形外。注意射线如果在端点处和多边形相交,或者穿过一条完整的边,则需要重新引出一条射线QAQ

    method2 转角法

    我们把多边形的每条边的转角加起来,如果是360度,说明在多边形内。如果是0度,说明在多边形外。如果是180度,说明在多边形的边界上。优化的操作如下:假想有一条向右的射线,统计多边形穿过这条射线正反多少次,把这个数基座绕数,逆时针穿过时+1,顺时针穿过时-1.

    旋转卡壳

    半平面交

    咕咕咕

  • 相关阅读:
    Mac-修改hosts文件(映射IP,取代网络DNS功能)
    iOS-内购及订阅
    Win-Navicat Premium 15 Window安装激活教程(学习研究)
    iOS-KLGenerateSpamCode(记录图片配参)
    iOS-Button 图片与文字位置
    iOS-关于GCD信号量那些事儿
    Mac-MacOS降级(Mac系统降级,系统回退)
    Xcode-一些小问题(配置包路径,配置文件路径。。。)
    Mac-App Store 购买过程中出错 请求超时
    2019 工作总结(APP组)
  • 原文地址:https://www.cnblogs.com/fengxunling/p/10010136.html
Copyright © 2011-2022 走看看