zoukankan      html  css  js  c++  java
  • 「数学」计算几何

    计算几何

    基础知识

    点积

    (a·b=a.x*b.x+a.y*b.y)

    (a)(b)上的投影乘以(b)的模长

    叉积

    (a×b=a.x*b.y-a.y*b.x)

    (a,b)围成的平行四边形的有向面积

    直线与线段

    记录直线上一点和直线方向

    线段记录线段端点 或记录线段起点和向量终点

    判断线段相交(快速排斥实验与跨立实验)

    快速排斥实验:如果以两条线段为对角线的矩形不相交,则两条线段不相交,这是充分不必要条件

    跨立实验:由一条线段的端点向另一条线段两端点做叉积,判断符号是否一致,符号不一致则相交,但线段共线时失效

    两者结合万无一失

    海伦公式

    [p=frac{a+b+c}{2}\ S=sqrt{p(p-a)(p-b)(p-c)} ]

    三点共线

    ((x_1y_2-x_2y_1)+(y_1z_2-y_2z_1)+(z_1x_2-z_2x_1)=0)

    围成的三角形面积为(0)

    求线段交点

    直接解方程太麻烦了

    交点(E)在直线(CD)上,根据三点共线的向量公式,(vec{OE}=kvec{OC}+(1-k)vec{OD})

    (k:1-k=|CE|:|DE|=S_{▲ABC}:S_{▲ABD})

    三角形面积可以用叉积求

    交点即为(C)的坐标加上(frac{1}{k})乘以(CD)的斜率

    点旋转

    旋转( heta)

    [x'=xcos heta-ysin heta\ y'=xsin heta+ycos heta ]

    向量旋转

    逆时针旋转(k)

    inline vector turn (vector a,double k)
    {
    	return vector(a.x*cos(k)-a.y*sin(k),a.x*sin(k)+a.y*cos(k));
    }
    
    

    判断点在多边形内部

    光线投射算法:由一点引一条射线,若与多边形有奇数个交点则在多边形内部

    回转数算法:

    原理:平面内闭合曲线绕过该点总次数为(0)时点在曲线外部

    实现:把一点和多边形所有顶点连起来,计算相邻两边有向夹角和,如果夹角和为不为零则在多边形内部

    求任意多边形面积

    将多边形上的点逆时针排序,选第一个点为辅助点(O),设(v_i=p_i-O),则

    [S=frac{1}{2}sum |v_i×v_{i mod n+1}| ]

    极角排序

    在平面内取一个定点(O),叫做极点,引一条射线(Ox),叫做极轴,再选定一个长度单位和角度的正方向(通常取逆时针方向)

    对于平面内任何一点(M),用(ρ)表示线段(OM)的长度,用( heta)表示(Ox)(OM)的角度,(ρ)叫做点(M)的极径,( heta)叫做点(M)的极角,有序对((ρ, heta))就叫点(M)的极坐标

    极角排序方法1:

    (atan2(y,x))函数按极角从小到大排序

    inline bool cmp (node a,node b)
    {
        double x=atan2(a.y,a.x),y=atan2(b.y,b.x);
        return x!=y?x<y:a.x<b.x;
    }
    

    曼哈顿距离

    (d(A,B)=|x_1-x_2|+|y_1-y_2|)

    满足三角不等式(d(i,j)leq d(i,k)+d(k,j))

    平面最远曼哈顿距离:求(max{|x_1-x_2|+|y_1-y_2|})

    假设(x_1>=x_2)

    (y_1>=y_2)时,(dis=x_1+y_1-(x_2+y_2))

    (y_1<y_2)时,(dis=x_1-y_1-(x_2-y_2))

    只要求(max{max(x_i+y_i)-min(x_i+y_i),max(x_i-y_i)-min(x_i-y_i)})

    或转切比雪夫距离,预处理横纵坐标最大值

    高维最大曼哈顿距离:坑

    切比雪夫距离

    (d(A,B)=max(|x_1-x_2|,|y_1-y_2|))

    曼哈顿与切比雪夫距离互化

    (A,B)曼哈顿距离:

    [egin{aligned} d(A,B)&=|x_1-x_2|+|y_1-y_2|\ &=max{x_1-x_2+y_1-y_2,x_1-x_2+y_2-y_1,x_2-x_1+y_1-y_2,x_2-x_1+y_2-y_1}\ &=max{|(x_1+y_1)-(x_2+y_2)|,|(x_1-y_1)-(x_2-y_2)|} end{aligned} ]

    这就是((x_1+y_1,x_1-y_1))((x_2+y_2,x_2-y_2))的切比雪夫距离

    曼哈顿距离转切比雪夫距离:((x,y)Rightarrow(x+y,x-y))

    新坐标下的切比雪夫距离即为原坐标系下的曼哈顿距离

    同理由上式推出,(A,B)的切比雪夫距离:

    [egin{aligned} d(A,B)&=max{|x_1-x_2|,|y_1-y_2|}\ &=max{|frac{x_1+y_1}{2}-frac{x_1-y_1}{2}|+|frac{x_2+y_2}{2}-frac{x_2-y_2}{2}|} end{aligned} ]

    ((frac{x_1+y_1}{2},frac{x_1-y_1}{2}))((frac{x_2+y_2}{2},frac{x_2-y_2}{2}))之间的曼哈顿距离

    切比雪夫距离转曼哈顿距离:((x,y)Rightarrow(frac{x+y}{2},frac{x-y}{2}))

    圆的树形结构

    规定内含与圆(A)的圆(B)(A)的儿子

    建树做法:将圆分为上下两个半圆,扫描到上半圆时将该半圆所属圆建立子树并入栈,扫描到下半圆时回溯

    二维凸包

    求凸包

    (Graham)算法:

    二维平面上给定(n)个点求凸包

    显然左下角的点应该在凸包上

    (y)最小的点同时(x)最小的点(左下角的点)先选出来,将其他点以左下角的点为基准点极角排序

    按极角序将点入栈,利用斜率单调性判断是否弹栈

    注意极角排序时若极角相同应该让距基准点较近的点放在前面,保证会被弹出

    (Andrew)算法:

    将所有点按(x)从小到大排序

    从第一个点开始,如果下一个点在当前直线的左侧,则入栈

    否则将上一个点出栈,再判断

    这样一次遍历只会求出一个下凸壳,如果要得到整个凸包要从最后一个点再反向遍历一遍

    动态二维凸包

    Pick定理

    对于顶点均为整点的简单多边形,其面积(S),内部格点数(i),边上格点数(b)满足

    (S=i+frac{b}{2}-1)

    推广:

    在格点面积为(1)的空间中,格点是平行四边形则皮克定理适用

    格点是三角形则(S=2*i+b-2)

    旋转卡壳

    求凸包直径:

    首先不能用双指针法扫描点对

    考虑以下情况:

    此时并不能保证点对之间距离是单调增的

    改变一下思路,如果一个点到一条边的距离最远,那么它到这条边的两个端点中的一个一定是最远的

    怎么找距离一条边最远的点(或距离一个点最远的边)呢?

    考虑旋转卡壳

    如果我们枚举边,那么一条边和另外一点形成的三角形的底固定,则三角形面积越大,点和直线距离越大,所以我们可以用叉积快速算面积,通过双指针找到每条边的面积峰值

    这个过程就像有两条斜率相等的直线,一条直线与凸包的一条边重合,另一条直线在凸包的另一个刚好卡住了一个距离该直线最远的顶点

    半平面交

    给定(n)条直线,每条直线留下左半部分,求最后围成的面积

    按照计算几何惯例先将直线按(x)轴夹角排序,夹角相同保留靠左的

    用一个双端队列维护半平面交

    维护方法:先保证队列中至少有两个元素

    如果队尾两个直线的交点在新入队直线的右侧

    那么由于新入队直线与(x)轴夹角一定更大,所以可以直接把队尾弹出

    队首同理

    但是记住一定要先弹出队尾

    假如图中直线标号是入队顺序,当(3)入队时先判断(1)就会先弹出(1),先判断(2)就会先弹出(2),但显然与队首围成的面积是更小的

    最后还要对首尾再检测,防止出现如下情况

    代码细节很多

    #include<bits/stdc++.h>
    using namespace std;
    namespace red{
    #define ls(p) (p<<1)
    #define rs(p) (p<<1|1)
    #define mid ((l+r)>>1)
    #define lowbit(i) ((i)&(-i))
    #define eps (1e-9)
    	inline int read()
    	{
    		int x=0;char f=0,ch;
    		for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    		if(ch=='-') f=1,ch=getchar();
    		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    		return f?-x:x;
    	}
    	const int N=5010;
    	int n,m,tot,sum,head,tail;
    	struct node
    	{
    		double x,y;
    		inline double operator ^ (const node &t) const{return x*t.y-y*t.x;}
    		inline node operator + (const node &t) const{return (node){x+t.x,y+t.y};}
    		inline node operator - (const node &t) const{return (node){x-t.x,y-t.y};}
    		inline node operator * (const double &t) const{return (node){x*t,y*t};}
    	}a[N],c[N];
    	struct line
    	{
    		node a,b;//a是向量起始点,b是以a为原点向量的终点 
    		double k;
    		line(){}
    		line(const node &aa,const node &bb):a(aa),b(bb){k=atan2(b.y,b.x);}
    		inline bool operator < (const line &t) const{return k<t.k;}
    	}f[N],q[N];
    	inline double cross(node a,node b,node c){return (b-a)^(c-a);}//跨立 
    	inline node getnode(line A,line B)//面积比例求交点 
    	{
    		node C=A.a-B.a;//新起点 
    		double t=(B.b^C)/(A.b^B.b);
    		return A.a+A.b*t; 
    	}
    	inline int dcmp(double x)
    	{
    		if(fabs(x)<eps) return 0;
    		return x>0?1:-1;
    	} 
    	inline double are()
    	{
    		double are=0;
    		for(int i=head;i<tail;++i) are+=fabs(cross(c[head],c[i],c[i+1]));
    		return are/2.0;
    	}
    	inline void work()
    	{
    		head=tail=1;
    		q[tail]=f[1];
    		//c[i]表示a[i]和a[i+1]的交点 
    		for(int i=2;i<=sum;++i)
    		{
    			while(head<tail&&(f[i].b^(c[tail-1]-f[i].a))<=eps) --tail;
    			while(head<tail&&(f[i].b^(c[head]-f[i].a))<=eps) ++head;
    			q[++tail]=f[i];
    			if(!dcmp(q[tail].b^q[tail-1].b))//平行保留靠左的 
    			{
    				--tail;
    				if(dcmp(q[tail].b^(f[i].a-q[tail].a))==1) q[tail]=f[i];
    			}
    			if(head<tail) c[tail-1]=getnode(q[tail-1],q[tail]);
    		}
    		while(head<tail&&(q[head].b^(c[tail-1]-q[head].a))<=eps) --tail;//防止最后尾部绕出头部 
    		if(tail-head<=1) return;
    		c[tail]=getnode(q[head],q[tail]);
    	}
    	inline void main()
    	{
    		n=read();
    		for(int i=1;i<=n;++i)
    		{
    			m=read();
    			for(int j=1;j<=m;++j)
    			{
    				a[j].x=read(),a[j].y=read();
    			}
    			a[0]=a[m];
    			for(int j=0;j<m;++j)
    			{
    				int t=j+1;
    				f[++sum]=line(a[j],a[t]-a[j]);
    			}
    		}
    		sort(f+1,f+sum+1);
    		work(); 
    		printf("%.3f
    ",are());
    	}
    }
    signed main()
    {
    	red::main();
    	return 0;
    }
    
  • 相关阅读:
    浏览器图片渲染优化教程
    C#把 DataTable转换为Model实体
    .NET静态变量与静态方法并发的问题
    三种方法查看MySQL数据库的版本
    Mysql 存储引擎 InnoDB与Myisam的主要区别
    dotnet core 出现Can not find runtime target for framework '.NETCoreApp,Version=v1.6' 的解决办法
    细说 C# 中的 IEnumerable和IEnumerator接口
    ASP.NET CORE dotnet run 命令使用debug方式运行
    安装了插件情况下 强制开启火狐浏览器的多进程功能
    JSON 字符串中的中括号和大括号区别详解
  • 原文地址:https://www.cnblogs.com/knife-rose/p/15457340.html
Copyright © 2011-2022 走看看