【ACM】 计算几何

前言

因为没有拿到牌子,心情低迷,随缘更新..

计算几何

目录

1、平面几何前置知识

///非科学计数法输出 cout.setf(ios::fixed); ///设置输出小数点 cout << setprecision(4) << x <<endl;

2、点

2.1、点的表示

在计算几何的题目中,基本上都是实数计算,所以我们一般都使用精度更高的 double 来进行计算。double 类型的数据在读入的时候要使用 %lf ,在输出的时候要使用 %f

struct Point { double x,y; Point(){} /// 无参构造 Point(double _x,double _y):x(-x),y(_y){} /// 有参构造 }P[N];

2.2、两点之间的距离

两点距离公式: dis=(x1x2)2+(y1y2)2

double distance(Point P){ hypot(x-P.x,y-P.y); /// 库函数,求根号下,两数平方和 }

3、向量

3.1、计算机中的向量

我们一般表示一个向量也是直接用一个点来表示,方向是从原点指向(x,y)坐标

比如说 Point (4,2) 这个点就表示的是从原点指向 B 的一个向量,因为向量是可以平移的,所以为了方便计算,我们直接用一个点来表示一个向量

I3Kkm4.png

在计算中的,我们处理区间的运算,多数是在坐标原点处理的,可能他真实的情况是 (A1,C1)(A2,B2)​​ 这样的情况,但是我们都将其平移到原点处理

I8ZrOH.png

3.1、点积(Dot Product)

向量的基本运算是 点积叉积 ,计算几何的各种操作几乎都基于这两种运算。

已知两个 a,b​ 向量 ,它们的夹角为 θ​ ,那么:ab=|a||b|cosθ

就是这两个向量的 数量积,也叫 点积内积 。点积的几何意义为: ab 上的投影乘以 b​ 的模长

IGpXn0.png

3.1.1、向量间的投影

由点积公式:ab=|a||b|cosθ

double operator * (Point b){ return x*b.x + y*b.y; }

我们可以发现 |a|cosθ​​ ,|b|cosθ​​​​​ 都为一个向量到另一个向量的投影。

我们就可以通过点积来计算相互之间的投影:

|b|cosθ=ab|a|​​

|a|cosθ=ab|b|​​

3.1.2、点积与cosθ

夹角 θ​ 与点积大小的关系:

(补充cos的图)

  1. θ=0°​​​​ ​,ab=|a||b|​​ ,向共线
  2. θ=180°​ ,ab=|a||b|​ ,向向共线
  3. θ=90°​ ,ab=0​ ,垂直
  4. θ<90°​​ ,ab>0​​ ,两向量的夹角为锐角
  5. θ>90°ab<0 ,两向量的夹角为钝角

从上面我们可以发现,我们可以通过点积的结果来判断,共线的方向,以及垂直

3.2、叉积(Cross Product)

我们定义向量 a,b​ 的向量积为一个向量,记为 a×b ,其模与方向定义如下:

  1. |a×b|=|a||b|sin<a,b>​​
  2. a×ba,b 都垂直,且 a×ba,b​​​ 都符合右手法则。

我的右手和官方的不太一样,平摊(正面或者反面朝上)右手,大拇指和第一个向量方向相同,四指指向另一个(如果别扭就翻过来)。

手心向上,大于零,手心向下,小于零。我觉得这样方便点。

向量积也叫外积,也叫叉积。

double operator ^ (Point b){ return x*b.y - y*b.x; }

3.2.1、求平行四边形面积

然而注意到向量积的模,联想到三角形面积计算公式 S=12absinC​ ,我们可以发现向量积的几何意义是: |a×b|​ 是以为 a,b​​ 为邻边的平行四边形的面积

3.2.2、判断A,B向量方向关系

a×b>0​​​ ,b​​​ 在 a​​ 的逆时针方向;

a×b<0​ ,b​ 在 a​​ 的顺时针方向;

a×b=0​ , b​ 与 a​​​ 可能同向共线,也可能反向共线(刚才的点积就可以判断是同向共线还是反向共线),还可能平行。

3.4、向量的运算

3.4.1、向量模长

向量模长,就是原点到 (x,y) 坐标的距离(在我们的上述的建模方式下),对于向量 a=(x,y)|a|=(x0)2+(y0)2 ,所以我们用与求两点距离的方式来求向量的模长。

double len(){ return hypot(x,y); } /// 长度的平方,便于之后的计算 double len2(){ return x*x + y*y; }

3.4.2、向量加、减

上图可以看到 AC+AB 的值就是坐标 D(Cx+Bx,Cy+By)

同时可以看到 ABAC 的值就是坐标 CB(BxCx,ByCy)

Point operator + (Point b){ return Point( x+b.x,y+b.y ); } Point operator - (Point b){ return Point( x-b.x,y-b.y ); }

( 注:这些 operator 都是直接重载的结构体运算符,如果没有这方面的前置知识,请先学习如果重载结构体的运算符 )

3.4.3、向量数乘(放缩)

对于 a=(x,y)λa=(λx,λy) ,除法的话等价于aλ=1λa=(1λx,1λy)

Point operator * (double k){ return Point(k*x,k*y); } Point operator / (double k){ return Point(x/k , y/k); }

对于数乘的具体应用是,把一个向量变为长度为 r 的向量,但是向量方向不变

Point trunc(double r){ double l = len(); r /= l; return Point(x*r,y*r); }

3.4.4、判断向量相等

因为计算机运行精度问题,所以我们不能直接用 == 来判断 double 类型的数据是否相等,这里引入,eps 来表示题目允许的误差范围,sgn 来代替比较运算符。

const double eps = 1e-8; int sgn(double x) { if(fabs(x) < eps ) return 0; if( x < 0 ) return -1; else return 1; }

1e-8 表示的是1×108 是判断两值相等所允许的误差范围,比如 1.0000000021.000000005 被视为相等的两个数,均表示 1.00000000 ,如果两数相减的值,小于 eps 那么我们就视为这是两个相等的数,基于此,我们可重载 <== 运算符。小于运算符的排序规则是,x 小的,y 小的在前,x作为第一标准,y作为第二标准。

bool operator == (Point b) { return sgn(x-b.x) == 0 && sgn(y-b.y) == 0; } bool operator < (Point b){ return sgn(x-b.x)==0?sgn(y-b.y)<0:b.x; }

3.4.5、两向量夹角

前缀知识:

  1. atan(x)表示求的是 x 的反正切,其返回值为[π2,+π2]之间的一个数。

  2. atan2(y,x)求的是 y/x 的反正切,其返回值为[π,+π]之间的一个数。

kuangbin用的方法是

double rad(Point a,Point b){ Point p = *this; return fabs( atan2( fabs( (a-p)^(b-p) ) , (a-p)*(b-p) ) ); }

我看了一眼没有理解,就直接推了一遍,并测试了在精度为 esp=1e-10的情况下,两则的答案相同

我自己的公式推导:

对于两个从原点出发的向量

由点积公式可得: x1y1+x2y2=|A||B|cosθ​​​​ ,由模长公式和反三角函数可得 θ=\acos(x1y1+x2y2x12+y12×x22+y22)​​​​

/// 求 a,b 向量对于原点的夹角 /// 如果是三个数的夹角的话,this 为中间的那个点,a,b为两端 /// return fabs( acos( ((a-tp)*(b-tp))/( (a-tp).len() * (b-tp).len() ) ) ) double rad(Point a,Point b){ Point tp = *this; return fabs(acos( (tp*b)/(tp.len() * b.len()) )); }

哦哦哦,我明白了,kuangbin的是这样推的,还是推荐用kuangbin的因为代码更加简介,我的推导只用了点积(之后会讲解),或者也可以只用叉积,但是如果我们把叉积点积 相除,就会得到 kuangbin 的式子,下面为推导。

x1y2x2y1=|A||B|sinθ

x1y1+x2y2=|A||B|cosθ​ ,相除可得

sinθcosθ=x1y2x2y1x1y1+x2y2​​,进一步推导 tanθ=aba×b​​,然后求个反三角函数就好了,之后学习了点积和叉积的计算,同时我们重载了运算符之后,就会发现用这个方法来实现求夹角,代码就会比较精简。

kuangbin yyds!

3.4.6、向量旋转

a=(x,y)​ ,倾角为 θ​ ,长度为 l=x2+y2​ 。则 x=lcosθ,y=lsinθ​ 。令其逆时针旋转 α​ ​度角,得到向量 b=(lcos(θ+α),lsin(θ+α))​ 。

由三角恒等变换得,b=(l(cosθcosαsinθsinα),(sinθsinα+cosθsinα))

x,y 的值带入式子得到,b=(xcosαysinα,ycosα+xsinα)

IJNGxe.png

绕着 P 点旋转

Point rotata(Point p,double angle){ Point v = (*this) - p; double c = cos(angle) , s = sin(angle); return Point(p.x + v.x * c - v.y * s , p.y + v.x *s + v.y * c); /// 绕原点旋转后,加上原来P点的偏移量 }

对于特殊的选择 90°,我们知道 cos90°=0​ 所以就直接变成了

///逆时针旋转90度 Point rotleft(){ return Point(y,-x); } ///顺时针旋转90度 Point rotright(){ return Point(y,-x); }

3.5、向量板子

struct Point { double x,y; double angle; int id; Point(){} /// 无参构造 Point(double _x,double _y){ x = _x; y = _y; } /// 有参构造 /// 向量加法 Point operator + (const Point b) const{ return Point( x+b.x,y+b.y ); } /// 向量乘法 Point operator - (const Point b) const{ return Point( x-b.x,y-b.y ); } /// 向量数乘 Point operator * (const double k) const{ return Point(k*x,k*y); } /// 向量数除 Point operator / (const double k) const{ return Point(x/k , y/k); } bool operator == (const Point b) const{ return sgn(x-b.x) == 0 && sgn(y-b.y) == 0; } bool operator < (const Point b) const { return sgn(x-b.x)==0?sgn(y-b.y)<0:x<b.x; } /// 点积 double operator * (const Point b) const{ return x*b.x + y*b.y; } /// 叉积 double operator ^ (const Point b) const{ return x*b.y - y*b.x; } /// 两点之间的距离 double distance(const Point P) const { return hypot(x-P.x,y-P.y); /// 库函数,求根号下,两数平方和 } /// 向量长度 double len(){ return hypot(x,y); } /// 长度的平方,便于之后的计算 double len2(){ return x*x + y*y; } /// 化为长度为 r 的向量 Point trunc(double r){ double l = len(); r /= l; return Point(x*r,y*r); } /// 以 p 为中心点,pa,pb的夹角大小 double rad(Point a,Point b){ Point p = *this; return fabs( atan2( fabs( (a-p)^(b-p) ) , (a-p)*(b-p) ) ); } /// 绕 p点 逆时针选择 angle 度 Point rotate(Point p,double angle){ Point v = (*this) - p; double c = cos(angle) , s = sin(angle); return Point(p.x + v.x * c - v.y * s , p.y + v.x *s + v.y * c); /// 绕原点旋转后,加上原来P点的偏移量 } ///逆时针旋转90度 Point rotleft(){ return Point(y,-x); } ///顺时针旋转90度 Point rotright(){ return Point(y,-x); } };

4、点与直线

4.1、直线,线段的表示

在计算几何中,直线,线段我们都用两个 Point 来表示,如果把 Point 视为点的话,那么所组成的就是线段,如果把 Point 视为向量的话,那么所组成的就是直线。

struct Line { Point s,e; Line(){} Line(Point _s,Point _e):s(_s),e(_e){} };

4.1.1、由解析式来表示直线

  1. 斜截式式 y=kx+b
  2. 标准方程 ax+by+c=0

通常,我们采用斜截式的时候是用直线与 x​ 轴的顺时针倾斜角与直线上一点来得到两点。

我们设直线的斜倾角为 θ ,斜率为 k ,已知两点 P1(x1,y1),P2(x2,y2)

k=y1y2x1x2​ ,tanθ=y1y2x1x2=k​​ ,那么θ=atan(k)​。

Line(Point p,double angle){ s = p; if( sgn(angle - pi/2) == 0 ) e = (s + Point(0,1)); else e = (s + Point(1,tan(angle))); } ///ax + by + c = 0; Line(double a,double b,double c){ if( sgn(a) == 0 ) { s = Point(0,-c/b); e = Point(1,-c/b); } else if(sgn(b) == 0) { s = Point(-c/a,0); e = Point(-c/a,1); } else { s = Point(0,-c/b); e = Point(1,(-c-a)/b); } }

4.1.2、线段长度

就是两点之间的距离

double length(){ return s.distance(e); }

4.1.3、直线基于 x 轴的斜倾角

就是用 4.1.1 中的公式来求

注:const doubel pi = acos(-1);

double angle(){ double k = atan2(e.y-s.y , e.x-s.x); if( sgn(k) < 0 ) k += pi; if( sgn(k-pi) == 0 ) k -=pi; return k; }

4.2、点与直线

4.2.1、点与直线的位置关系

IYKbvt.png

在二维平面上,点和直线有 3 种位置关系,点在直线左侧点在直线右侧点在直线上。记点为 P,直线上的点为 P1,P2 ,取 P1PP1P2 构成向量用叉积就能得到位置关系。

///点与直线关系 ///1在左侧 ///2在右侧 ///3在直线 int relation(Point p){ int c = sgn( (p-s) ^ (e -s) ); if(c < 0) return 1; else if(c > 0) return 2; else return 3; }

4.2.2、点到直线的距离

IYo7Jx.png

如图,我们设点 P0,取直线点 P1,P2,我们用P0P1P2P1 来将 P1 点移动到原点,我们要求的点到直线的距离的长度设为 lP0P1P2P1 两个向量的夹角设为 θ

通过叉积公式 a×b=|a||b|sinθ

我们可以得到 |b|sinθ=a×b|a|=l​​,这样我们就得到了点到直线的距离的计算公式,因为叉积是有正负的,所以最后要取个绝对值。

double dispointtoline(Point p){ return fabs( (p-s)^(e-s) ) / length(); }

4.2.3、点在直线上的投影(垂足)

还是上面哪个图,这次我们是要求 |b|cos|θ|​​ , 我自己想的就是直接用点积来求,但是 kuangbin黑书 都是用的成比例的方式,是因为求 cos 损失精度?需要 fabs

...

我去模拟了一下,才发现 lineProg 这个函数的返回值是 Point !!!所以这波是数学不过关。 我之前一直理解的是标量投影。但是这里我们求的是矢量投影,见百度百科。

IYqIGn.png

P0​​​ 到 P1P2​​​ 的垂足为 P3​​​ (均为向量),为了计算不用cos 所以我们要求一个 k=|P3P1||P2P1|​​​ ,长度比例嘛, P3​​​ 就是我们要求的 矢投影

由点积的公式:(P0P1)(P2P1)=|P0P1||P2P1|cosθ​ ,我们把 P3 引入

公式变为 : (P0P1)(P2P1)=|P2P1||P3P1|​​

带入 k 的式子中: k=|P3P1||P2P1|=(P0P1)(P2P1)|P2P1||P2P1|

所以,P3=P1+k(P2P1)

/// 返回点p在直线上的投影 Point lineprog(Point p){ return s + ( ( (e-s)*((e-s)*(p-s)) ) / ( (e-s).len2() ) ); }

这个也想相当于是,求点到直线的垂足向量

4.2.4、点关于直线的对称点

求一个点对一条直线的对称点。先求点 P 在直线上的投影点 P3 (承接上一小节),再求对称点 。

就是把垂足的长度延长一倍就好了。

Point symmetrypoint(Point p){ Point q = lineprog(p); return Point(2*q.x-p.x,2*q.y-p.y); }

一组测试数据

int main() { Point p = Point(4,5); Line l = Line( Point(0,0),Point(1,10) ); Point te = l.lineprog(p) ; printf("%.10f\n%.10f\n",te.x,te.y); Point t = l.symmetrypoint(p); printf("%.10f\n%.10f",t.x,t.y); return 0; } /* 输出 0.5346534653 5.3465346535 -2.9306930693 5.6930693069 */

4.3、点与线段

4.3.1、点与线段的位置关系

判断点 p​ 是否在线段上, 先用叉积判断是共线,再判断是否与端点组成的线段与原线段形成钝角。

3.1.2 节,我们知道:

θ=180°ab=|a||b|向向共线

同时点 p​ 还有可能在端点上,所以最后为 0

bool point_on_seg(Point p){ return sgn((p-s)^(e-s) ) == 0 && sgn( (p-s)*(p-e) ) <= 0 ; }

4.3.2、点到线段的距离

点到线段的距离和点到直线的距离有所不同,设点为 P ,线段AB

P到AB的垂线长度P到A的距离P到B的距离,三者中取最小值。It7o1P.png

这是 if 判断的情况

double dispointtoseg(Point p){ if( sgn((p-s)*(e-s)) < 0 || sgn((p-e)*(s-e))<0 ) return min( p.distance(s),p.distance(e) ); return dispointtoline(p); }

5、直线与线段

5.1、直线与直线的位置关系

5.1.1、判断直线与直线平行

3.2.2小节,我们知道:

a×b=0ba​ 可能同向共线,也可能反向共线(刚才的点积就可以判断是同向共线还是反向共线),还可能平行。

bool parallel(Line v){ return sgn( (e-s)^(v.e-v.s) ) == 0 ; }

5.1.2、两直线位置关系

思路就是,先判断平行,再判断是否共线,如果都不是的话那肯定是相交了。

这里共线我们直接用点与直线的共线来判断。

/// 判断两直线的位置关系 ///0 平行 ///1 共线 ///2 相交 int linecrossline(Line v){ if( v.parallel(*this) ) return v.relation(s) == 3; return 3; }

5.2、直线与线段的位置关系

(-1)^(1) = 2

/// 直线与线段位置关系 -*this line -v seg //2 规范相交 //1 非规范相交(顶点处相交) //0 不相交 int linecrossseg(Line v){ int d1 = sgn( (e-s)^(v.s-s) ); int d2 = sgn( (e-s)^(v.e-s) ); if( (d1 ^ d2) == -2 ) return 2; return (d1==0||d2==0); }

5.3、线段与线段的位置关系

IUi9yD.png

可用上图数据进行验证

///两线段相交判断 ///2 规范相交 ///1 非规范相交 ///0 不想交 int segcrossseg(Line v) { int d1 = sgn((e - s) ^ (v.s - s)); int d2 = sgn((e - s) ^ (v.e - s)); int d3 = sgn((v.e - v.s) ^ (s - v.s)); int d4 = sgn((v.e - v.s) ^ (e - v.s)); if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2)return 2; return (d1 == 0 && sgn((v.s - s) * (v.s - e)) <= 0) || (d2 == 0 && sgn((v.e - s) * (v.e - e)) <= 0) || (d3 == 0 && sgn((s - v.s) * (s - v.e)) <= 0) || (d4 == 0 && sgn((e - v.s) * (e - v.e)) <= 0); }

5.4、线段到线段的距离

double disssegtoseg(Line v){ return min( { dispointtoseg(v.s),dispointtoseg(v.e) ,v.dispointtoseg(s), v.dispointtoseg(e) } ); }

直线到直线的距离,只有当两直线平行的时候才有意义,在两直线平行的情况下,我们直接随便取一个直线的 s,e 然后求点到直线的距离就可以了。

5.5、两条直线的交点

两直线,两线段的求交点是一样的,但是都要先判断是否相交,再求交点

Point crosspoint(Line v){ double a1 = (v.e-v.s)^(s-v.s); double a2 = (v.e-v.s)^(e-v.s); return Point( (s.x*a2-e.x*a1)/(a2-a1) , (s.y*a2-e.y*a1)/(a2-a1) ); }

6、线板子

struct Line { Point s,e; Line(){} Line(Point _s,Point _e){ s =_s; e= _e; } /// 点斜 Line(Point p,double angle){ s = p; if( sgn(angle - pi/2) == 0 ) e = (s + Point(0,1)); else e = (s + Point(1,tan(angle))); } ///ax + by + c = 0; Line(double a,double b,double c){ if( sgn(a) == 0 ) { s = Point(0,-c/b); e = Point(1,-c/b); } else if(sgn(b) == 0) { s = Point(-c/a,0); e = Point(-c/a,1); } else { s = Point(0,-c/b); e = Point(1,(-c-a)/b); } } /// 线段长度 double length(){ return s.distance(e); } /// 返回 0 <= angle <= pi 的基于 x轴 的斜倾角 double angle(){ double k = atan2(e.y-s.y , e.x-s.x); if( sgn(k) < 0 ) k += pi; if( sgn(k-pi) == 0 ) k -=pi; return k; } ///点与直线关系 //1 在左侧 //2 在右侧 //3 在直线 int relation(Point p){ int c = sgn( (p-s) ^ (e -s) ); if(c < 0) return 1; else if(c > 0) return 2; else return 3; } /// 点到直线的距离 double dispointtoline(Point p){ return fabs( (p-s)^(e-s) ) /length(); } /// 返回点p在直线上的投影点(垂足) Point lineprog(Point p){ return s + ( ( (e-s)*((e-s)*(p-s)) ) / ( (e-s).len2() ) ); } /// 返回点p在直线上的对称点 Point symmetrypoint(Point p){ Point q = lineprog(p); return Point(2*q.x-p.x,2*q.y-p.y); } /// 点与线段的位置关系 bool point_on_seg(Point p){ return sgn((p-s)^(e-s) ) == 0 && sgn( (p-s)*(p-e) ) <= 0 ; } /// 点到线段的距离 double dispointtoseg(Point p){ if( sgn((p-s)*(e-s)) < 0 || sgn((p-e)*(s-e))<0 ) return min( p.distance(s),p.distance(e) ); return dispointtoline(p); } /// 判断平行 bool parallel(Line v){ return sgn( (e-s)^(v.e-v.s) ) == 0 ; } /// 判断两直线的位置关系 //0 平行 //1 共线 //2 相交 int linecrossline(Line v){ if( v.parallel(*this) ) return v.relation(s) == 3; return 3; } /// 直线与线段位置关系 -*this line -v seg //2 规范相交 //1 非规范相交(顶点处相交) //0 不相交 int linecrossseg(Line v){ int d1 = sgn( (e-s)^(v.s-s) ); int d2 = sgn( (e-s)^(v.e-s) ); if( (d1 ^ d2) == -2 ) return 2; return (d1==0||d2==0); } ///两线段相交判断 ///2 规范相交 ///1 非规范相交 ///0 不想交 int segcrossseg(Line v) { int d1 = sgn((e - s) ^ (v.s - s)); int d2 = sgn((e - s) ^ (v.e - s)); int d3 = sgn((v.e - v.s) ^ (s - v.s)); int d4 = sgn((v.e - v.s) ^ (e - v.s)); if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2)return 2; return (d1 == 0 && sgn((v.s - s) * (v.s - e)) <= 0) || (d2 == 0 && sgn((v.e - s) * (v.e - e)) <= 0) || (d3 == 0 && sgn((s - v.s) * (s - v.e)) <= 0) || (d4 == 0 && sgn((e - v.s) * (e - v.e)) <= 0); } /// 线段到线段的距离 double disssegtoseg(Line v){ return min( min( dispointtoseg(v.s),dispointtoseg(v.e)) , min(v.dispointtoseg(s), v.dispointtoseg(e) ) ); } /// 求两直线的交点 Point crosspoint(Line v){ double a1 = (v.e-v.s)^(s-v.s); double a2 = (v.e-v.s)^(e-v.s); return Point( (s.x*a2-e.x*a1)/(a2-a1) , (s.y*a2-e.y*a1)/(a2-a1) ); } };

6、多边形

之前的多数来自于 kuangbin计算几何板子黑书 ,之后的可能就是我自己想的很多方法了

6.1、多边形的表示

maxp 为点的数量,狂兵的把求凸包,求极角排序都写在 struct 里面了,但是我觉得这个应该是在 点集上进行的操作,所以我写在了外面。

const int maxp = 1e5+10; struct Polygon { int n; Point p[maxp]; Line l[maxp]; /// 在多边形中添加点 void add(Point q){ p[n++] = q; } /// 获取所有的线段 void getLine(){ for(int i = 0; i < n ; i++){ l[i] = Line(p[i],p[(i+1)%n]); } } };

6.2、多边形判定与求得

6.2.1、极角排序

对于一个二维平面上的点集,我们选择点集中,最左下角的点,做 J 点到 x轴 的平行线,为直线 JA,然后逆时针旋转 直线JA ,接触其他点的先后循序就是 极角排序 的结果。

IUw4tH.png


如果有两个点与 JA 都相交,那么我们定义与 J 的距离短的优先级高。

IUw7ct.png

6.2.1.1、atan2得到极角排序

3.4.5 中我们知道了 atanatan2 的区别,因为 atan2 的取值是在 [π,+π] 所以,我们用 atan2 来求,直线之间的夹角,因为极角排序其实就是夹角排序。

之前狂兵的板子中的 Point 是没有angle 属性的。我这里把极角排序从多边形中独立出来,多边形就只处理多边形那些关系,不管求凸包,极角排序这些,因为这些其实都是 点集 在干的事。

我调整了一下点的位置,把他们都变成了整数。

IU2CvQ.png

我先是这样写的 atan2 cmpAtan2Point 为最左下角的 J

te[i].angle = atan2( te[i].x - cmpAtan2Point.x,te[i].y - cmpAtan2Point.y );

然后我发现我是 x,y 写反了,最后他变成了 J与y轴平行,顺时针的过去!还有一个值得注意的问题:

atan2[π,+π] 所以,刚才的与 y轴平行 的情况中 B 点的值为负,反而排到前面了,所以计算 atan2 的时候小于零的数都 +2π​ 。​

同时要注意,在 Point中,我们之前重载的 <x小就小 x 的优先级比 y 高,这里我们需要提高 y 的优先级

修改的代码:

/// Point 的重载 bool operator < (Point b) const { return sgn(y-b.y)==0?sgn(x-b.x)<0:y<b.y; } /// -------------------------------------------------/// /// 用来指定排序的极点 Point cmpAtan2Point = Point(inf,inf); /// sort cmp bool cmpAtan2(Point a,Point b) { if( sgn(a.angle - b.angle) == 0 ) return a.distance(cmpAtan2Point) < b.distance(cmpAtan2Point ); return a.angle < b.angle; } Point te[N]; int main() { te[0] = {6,10}; te[1] = {8,2}; te[2] = {10,6}; te[3] = {10,14}; te[4] = {12,4}; te[5] = {12,8}; te[6] = {16,12}; te[7] = {18,10}; te[8] = {18,8}; te[9] = {18,4}; te[10] = {6,8}; /// 我又加了一个,来验证负数的情况 /// 取到最小的数,所以需要重载运算符 for(int i = 0; i < 11 ; i++){ cmpAtan2Point = min(cmpAtan2Point,te[i]); } cout << cmpAtan2Point.x << ' ' << cmpAtan2Point.y << endl; for(int i = 0; i < 11 ; i++){ if( te[i] == cmpAtan2Point ){ te[i].angle = 0; } else{ te[i].angle = atan2( te[i].x - cmpAtan2Point.x,te[i].y - cmpAtan2Point.y ); if( sgn(te[i].angle) < 0 ) te[i].angle += 2*pi; } } sort(te,te+11,cmpAtan2); for(int i = 0 ; i < 11 ; i++){ cout << te[i].angle <<endl; cout <<te[i].x << ' ' << te[i].y <<endl; } return 0; }

最后的结果:(注意这是 做J垂直于Y顺时针的极角排序

IUWmX4.png

修改为 J平行于X轴,逆时针扫

te[i].angle = atan2( te[i].y - cmpAtan2Point.y , te[i].x - cmpAtan2Point.x); ///只用修改这里

image-20211110142422341

6.2.1.2、叉积得到极角排序

这个比较老火,主要是用来排序,以左下角为极点的。

bool cmpCross(const Point a,const Point b) { double d = sgn((a-cmpPoint)^(b-cmpPoint)) ; if( d == 0 ) return sgn( a.distance(cmpPoint) - b.distance(cmpPoint) ) < 0; return d > 0; }

6.2.2、得到凸包

Gradham 扫描法的变种,Andrew 算法

///--------------------- 凸包 --------------------/// /// 求之前的点集 Point convexP[N]; int Sizep; /// 点集大小 void getconvex(Polygon &convex) { /// 按照 x 从小到大排序,如果 x 相同,按 y 排序。 sort(convexP,convexP+Sizep); convex.n = Sizep; for(int i = 0 ; i < min(Sizep,2) ; i ++){ convex.p[i] = convexP[i]; } /// 特判 if( convex.n == 2 && ( convex.p[0] == convex.p[1] ) ) convex.n--; if( Sizep <= 2 ) return ; int &top = convex.n; top = 1; for(int i = 2; i < Sizep ; i++){ while( top && sgn( (convex.p[top] - convexP[i])^(convex.p[top-1]-convexP[i]) ) <= 0 ) top--; convex.p[++top] = convexP[i]; } int temp = top; convex.p[++top] = convexP[Sizep-2]; for(int i = Sizep-3 ; i >= 0 ; i--){ while( top != temp && sgn( (convex.p[top] -convexP[i])^(convex.p[top-1]-convexP[i])) <= 0 ) top --; convex.p[++top] = convexP[i]; } if( convex.n == 2 && ( convex.p[0] == convex.p[1] ) ) convex.n--; /// 这样求出来的顺时针的凸包,如果要逆时针的话,来一次极角排序就好了。 /// 极角排序 cmpPoint = convex.p[0]; sort(convex.p,convex.p+convex.n,cmpCross); }

6.2.3、判定多边形是否为凸多边形

如果直接用点集来判断的话,要先求一波极角排序

bool isconvex(){ bool s[2]; memset(s,0,sizeof s); for(int i = 0 ; i < n ; i++){ int j = (i+1)%n; int k = (j+1)%n; s[ sgn( (p[j]-p[i])^(p[k]-p[i]))+1 ] = true; if( s[0] && s[2] ) return false; } return true; }

6.3、多边形周长

double getcircumference(){ double sum = 0; for(int i = 0 ; i < n ; i++){ sum += p[i].distance(p[(i+1)%n]); } return sum; }

6.4、多边形面积

如果是凹多边形,要按顺序排列才行。凸多边形,跑一边极角。

double getarea(){ double sum = 0; for(int i = 0; i < n ; i++){ sum += (p[i]^p[(i+1)%n]); } return fabs(sum)/2; }

6.5、多边形的重心

对于面积切割的加权平均。三角形的顶点重量都是分均的,但是多边形就不是了,所以要面积分均才是真正的重心。下图的这个5边形的重心要向上移,而不是在中间。

I0UqAO.png

Point getbarycenter(){ Point ret(0,0); double area = 0 ; for(int i = 1; i < n-1 ; i++){ double tmp = (p[i]-p[0])^(p[i+1]-p[0]); if( sgn(tmp) == 0 ) continue; area += tmp; ret.x += (p[0].x + p[i].x + p[i+1].x)/3*tmp; ret.y += (p[0].y + p[i].y + p[i+1].y)/3*tmp; } if( sgn(area)) ret = ret/area; return ret; }

6.6、点与多边形

6.6.1、点与多边形的位置关系

//3 点上 //2 边上 //1 内部 //0 外部 int relationpoint(Point q){ /// 在点上 for(int i = 0; i < n; i++){ if( p[i] == q ) return 3; } getLine(); /// 之前getline了的话,可以注释节约时间 /// 在边上 for(int i = 0; i < n ; i++){ if( l[i].point_on_seg(q) ) return 2; } /// 在内部 int cnt = 0; for(int i = 0 ; i < n ; i++){ int j = (i+1)%n; int k = sgn( (q-p[j])^(p[i]-p[j]) ); int u = sgn( p[i].y - q.y ); int v = sgn( p[j].y - q.y ); if( k > 0 && u < 0 && v >= 0 ) cnt ++; if( k < 0 && v < 0 && u >= 0 ) cnt--; } return cnt != 0; }

6.7、直线与多边形

  1. 判断线段 AB 是否在任意多边形 Poly 以内:不相交且两端点 A,B 均在多边形以内。

  2. 判断线段 AB 是否在凸多边形 Poly 以内:两端点 A,B​ 均在多边形以内。

6.8、多边形与多边形

判断任意两个多边形是否相离:属于不同多边形的任意两边都不相交 且 一个多边形上的任意点都不被另一个多边形所包含。

int relationpolygon(Polygon Poly){ getLine(); Poly.getLine(); for(int i1 = 0 ; i1 < n ; i1++){ int j1 = (i1+1)%n; for(int i2 = 0 ; i2 <= Poly.n ; i2++){ int j2 = (i2+1)%Poly.n; Line l1 = Line(p[i1],p[j1]); Line l2 = Line(Poly.p[i2],Poly.p[j2]); if( l1.segcrossseg(l2) ) return 0; if( Poly.relationpoint(p[i1]) || relationpoint(Poly.p[i2]) ) return 0; } } return 1; }

6.5、多边形与圆

6.6、多边形板子

struct Polygon { int n; Point p[maxp]; Line l[maxp]; /// 在多边形中添加点 void add(Point q){ p[n++] = q; } /// 获取所有的线段 void getLine(){ for(int i = 0; i < n ; i++){ l[i] = Line(p[i],p[(i+1)%n]); } } /// 判断多边形是不是凸包 /// 如果是直接对点集效验的话,要先极角排序 bool isconvex(){ bool s[2]; memset(s,0,sizeof s); for(int i = 0 ; i < n ; i++){ int j = (i+1)%n; int k = (j+1)%n; s[ sgn( (p[j]-p[i])^(p[k]-p[i]))+1 ] = true; if( s[0] && s[2] ) return false; } return true; } /// 多边形周长 double getcircumference(){ double sum = 0; for(int i = 0 ; i < n ; i++){ sum += p[i].distance(p[(i+1)%n]); } return sum; } /// 多边形面积 double getarea(){ double sum = 0; for(int i = 0; i < n ; i++){ sum += (p[i]^p[(i+1)%n]); } return fabs(sum)/2; } /// 重心 Point getbarycenter(){ Point ret(0,0); double area = 0 ; for(int i = 1; i < n-1 ; i++){ double tmp = (p[i]-p[0])^(p[i+1]-p[0]); if( sgn(tmp) == 0 ) continue; area += tmp; ret.x += (p[0].x + p[i].x + p[i+1].x)/3*tmp; ret.y += (p[0].y + p[i].y + p[i+1].y)/3*tmp; } if( sgn(area)) ret = ret/area; return ret; } /// 判断点与任意多边形的关系 //3 点上 //2 边上 //1 内部 //0 外部 int relationpoint(Point q){ /// 在点上 for(int i = 0; i < n; i++){ if( p[i] == q ) return 3; } // getLine(); /// 之前getline了的话,可以注释节约时间 /// 在边上 for(int i = 0; i < n ; i++){ if( l[i].point_on_seg(q) ) return 2; } /// 在内部 int cnt = 0; for(int i = 0 ; i < n ; i++){ int j = (i+1)%n; int k = sgn( (q-p[j])^(p[i]-p[j]) ); int u = sgn( p[i].y - q.y ); int v = sgn( p[j].y - q.y ); if( k > 0 && u < 0 && v >= 0 ) cnt ++; if( k < 0 && v < 0 && u >= 0 ) cnt--; } return cnt != 0; } /// 判断多边形是否与多边形相离 int relationpolygon(Polygon Poly){ getLine(); Poly.getLine(); for(int i1 = 0 ; i1 < n ; i1++){ int j1 = (i1+1)%n; for(int i2 = 0 ; i2 <= Poly.n ; i2++){ int j2 = (i2+1)%Poly.n; Line l1 = Line(p[i1],p[j1]); Line l2 = Line(Poly.p[i2],Poly.p[j2]); if( l1.segcrossseg(l2) ) return 0; if( Poly.relationpoint(p[i1]) || relationpoint(Poly.p[i2]) ) return 0; } } return 1; } }; /// 用来指定排序的极点 Point cmpPoint = Point(inf,inf); /// 需重载 Point 的小于符号 bool cmpAtan2(Point a,Point b) { if( sgn(a.angle - b.angle) == 0 ) return a.distance(cmpPoint) < b.distance(cmpPoint ); return a.angle < b.angle; } /// 慎用,第一个基本上就够了 bool cmpCross(const Point a,const Point b) { double d = sgn((a-cmpPoint)^(b-cmpPoint)) ; if( d == 0 ) return sgn( a.distance(cmpPoint) - b.distance(cmpPoint) ) < 0; return d > 0; } ///--------------------- 凸包 --------------------/// /// 求之前的点集 Point convexP[N]; int Sizep; /// 点集大小 void getconvex(Polygon &convex) { /// 按照 x 从小到大排序,如果 x 相同,按 y 排序。 sort(convexP,convexP+Sizep); convex.n = Sizep; for(int i = 0 ; i < min(Sizep,2) ; i ++){ convex.p[i] = convexP[i]; } /// 特判 if( convex.n == 2 && ( convex.p[0] == convex.p[1] ) ) convex.n--; if( Sizep <= 2 ) return ; int &top = convex.n; top = 1; for(int i = 2; i < Sizep ; i++){ while( top && sgn( (convex.p[top] - convexP[i])^(convex.p[top-1]-convexP[i]) ) <= 0 ) top--; convex.p[++top] = convexP[i]; } int temp = top; convex.p[++top] = convexP[Sizep-2]; for(int i = Sizep-3 ; i >= 0 ; i--){ while( top != temp && sgn( (convex.p[top] -convexP[i])^(convex.p[top-1]-convexP[i])) <= 0 ) top --; convex.p[++top] = convexP[i]; } if( convex.n == 2 && ( convex.p[0] == convex.p[1] ) ) convex.n--; /// 这样求出来的顺时针的凸包,如果要逆时针的话,来一次极角排序就好了。 /// 极角排序 // cmpPoint = convex.p[0]; // sort(convex.p,convex.p+convex.n,cmpCross); }

7、圆

7.1、圆的表示

struct Circle { Point c; double r; Circle(){} Circle(Point _c,double _r):c(_c),r(_r){} Circle(double x,double y,double _r){ /// 点的坐标,圆心 c = Point(x,y); r = _r; } bool operator == (Circle v){ return (p == v.p) && sgn(r-v.r) == 0; } /// 以圆心为主排序,半径为副排序 bool operator < (Circle v) const{ return ( (p<v.p) || (p == v.p) && sgn(r-v.r)<0 ); } /// 面积 double area(){ return pi*r*r; } /// 周长 double circumference(){ return 2*pi*r; } };

7.2、点和圆的关系

/// 点和圆的位置关系 //0 圆外 //1 圆上 //2 圆内 int relationPoint(Point b){ double dst = b.distance(p); if( sgn(dst-r)<0 ) return 2; if( sgn(dst-r)==0 ) return 1; return 0; }

7.3、直线和圆的关系

7.3.1、直线和圆的位置关系

比较的是圆心到直线的距离和半径的关系

//0 圆外 //1 圆上 //2 园内 int relationLine(Line v){ double dst = v.dispointtoline(p); if( sgn(dst-r) < 0 ) return 2; if( sgn(dst-r) == 1 ) return 1; return 0; }

7.3.2、直线和圆的交点

7.4、线段和圆的关系

比较的是圆心到线段的距离和半径的关系

int relationSeg(Line v){ double d = p.dispointtoSeg(p); if( sgn(dst-r) < 0 ) return 2; if( sgn(dst-r) == 0 ) return 1; return 0; }

7.5、圆与圆的关系

7.5.1、圆与圆的位置关系

//5 相离 //4 外切 //3 相交 //2 内切 //1 内含 int relationcircle(Circle v){ double d = p.distance(v.p); if( sgn( d-r-v.r ) > 0 ) return 5; if( sgn( d-r-v.r ) == 0 ) return 4; double l = fabs(r-v.r); if( sgn( d-r-v.r ) < 0 && sgn(d-l) > 0 ) return 3; if( sgn( d-l ) == 0 ) return 2; if( sgn( d-l ) < 0 ) return 1; }

7.5.2、圆与圆的交点个数

7.5.3、圆与圆的交点

7.5.4、两圆相交面积

7.6、直线和圆的交点

7.7、圆与三角形

7.7.1、三角形内接圆

7.7.2、三角形外接圆

7.7.3、圆与三角形相交面积

7.8、最小圆覆盖

8、专题

8.1、求凸包

6.2.2

8.2、最近点对

8.3、旋转卡壳

IrDFMD.png

参考博客

8.4、最远点对

8.4、半平面交

8.5、最小矩形覆盖

9、测试题

9.1、【LightOJ 1203】凸包+最小顶点夹角

9.2、【2020ICPC 昆明】线段与直线相交判断

这个题可以说是我计算几何的梦魇。今年上半年打昆明的时候,很快就发现了这个题是铜,但是到最后都没有过,今天知道原因了,没有排序的优先级有问题....

题意

给你一个点集,让你求任意一个点,与其他点所组成的直线与一条线段的第 k 个交点。保证没有3点共线。

题解

直接我们打个表,把所有的交点已经坐标都求出来,计算几何怎么搞都行,只要是基础的那些求交点,平行,都是O(1)​ 的世界复杂度。但是一定要对交点排序,排序的标准是距离线段第一个点的距离的长度!!

还有就是,计算几何别用 cin !!! 计算几何别用 cin !!! 计算几何别用 cin !!!

#include <bits/stdc++.h> using namespace std; const double eps = 1e-8; int sgn(double x) { if(fabs(x) < eps ) return 0; if( x < 0 ) return -1; else return 1; } const int N = 1e3+10; const int pi = acos(-1); //square of a double inline double sqr(double x){return x*x;} struct Point { double x,y; Point(){} /// 无参构造 Point(double _x,double _y):x(_x),y(_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 ); } /// 向量数乘 Point operator * (double k){ return Point(k*x,k*y); } /// 向量数除 Point operator / (double k){ return Point(x/k , y/k); } bool operator == (Point b) { return sgn(x-b.x) == 0 && sgn(y-b.y) == 0; } bool operator < (Point b){ return sgn(x-b.x)==0?sgn(y-b.y)<0:b.x; } /// 点积 double operator * (Point b){ return x*b.x + y*b.y; } /// 叉积 double operator ^ (Point b){ return x*b.y - y*b.x; } /// 两点之间的距离 double distance(Point P){ return hypot(x-P.x,y-P.y); /// 库函数,求根号下,两数平方和 } }P[N]; struct Line { Point s,e; Line(){} Line(Point _s,Point _e):s(_s),e(_e){} /// 直线与线段位置关系 -*this line -v seg //2 规范相交 //1 非规范相交(顶点处相交) //0 不相交 int linecrossseg(Line v){ int d1 = sgn( (e-s)^(v.s-s) ); int d2 = sgn( (e-s)^(v.e-s) ); if( (d1 ^ d2) == -2 ) return 2; return (d1==0||d2==0); } /// 求两直线的交点 Point crosspoint(Line v){ double a1 = (v.e-v.s)^(s-v.s); double a2 = (v.e-v.s)^(e-v.s); return Point( (s.x*a2-e.x*a1)/(a2-a1) , (s.y*a2-e.y*a1)/(a2-a1) ); } }; double t1,t2,t3,t4; vector<Point> ans[N]; bool cmp(Point a,Point b){ return a.distance(Point{t1,t2}) < b.distance(Point{t1,t2}); } int main() { int n,m; scanf("%d%d",&n,&m); scanf("%lf%lf%lf%lf",&t1,&t2,&t3,&t4); Line seg = Line(Point(t1,t2),Point(t3,t4)); for(int i = 1 ; i <= n ; i++){ scanf("%lf%lf",&P[i].x,&P[i].y); } for(int i = 1; i <= n ; i ++){ for(int j = 1; j <= n ; j++){ if(i ==j)continue; Line tel = Line(P[i],P[j]); if( tel.linecrossseg(seg) ){ Point tep = tel.crosspoint(seg); ans[i].push_back(tep); } } } for(int i = 1 ; i <= n ; i++){ sort(ans[i].begin(),ans[i].end(),cmp); } while(m--){ int te1,te2; scanf("%d%d",&te1,&te2); if( ans[te1].size() < te2 ){ puts("-1"); continue; } printf("%.6f %.6f\n",ans[te1][te2-1].x,ans[te1][te2-1].y); } return 0; }

9.3、【POJ - 2007】极角排序

#include <iostream> #include <cmath> #include <stdio.h> #include <algorithm> using namespace std; const double eps = 1e-8; const double inf = 1e14; int sgn(double x) { if(fabs(x) < eps ) return 0; if( x < 0 ) return -1; else return 1; } struct Point { double x,y; double angle; Point(){} /// 无参构造 Point(double _x,double _y){ x = _x; y = _y; } /// 有参构造 /// 向量加法 Point operator + (Point b) const{ return Point( x+b.x,y+b.y ); } /// 向量乘法 Point operator - (Point b) const{ return Point( x-b.x,y-b.y ); } bool operator == (Point b) const{ return sgn(x-b.x) == 0 && sgn(y-b.y) == 0; } bool operator < (Point b) const { return sgn(x-b.x)==0?sgn(y-b.y)<0:x<b.x; } /// 叉积 double operator ^ (Point b) const{ return x*b.y - y*b.x; } }P; /// 用来指定排序的极点 Point cmpPoint = Point(inf,inf); bool cmpCross(const Point &a,const Point &b) { double d = (a-cmpPoint)^(b-cmpPoint) ; // if( sgn(d) == 0 ) return sgn( a.distance(cmpPoint) - b.distance(cmpPoint) ) < 0; return d >= 0; } Point te[100]; int main() { int it = 0; while(scanf("%lf%lf",&te[it].x,&te[it].y) != EOF) ++it; cmpPoint = Point(0,0); sort(te+1,te+it,cmpCross); for(int i = 0 ; i < it ; i++){ printf("(%.0f,%.0f)\n",te[i].x,te[i].y); } return 0; } /* 80 20 50 -60 0 0 70 -50 60 30 -30 -50 90 -20 -30 -40 -10 -60 90 10 */

9.4、【CF 598C】极角排序

9.5、【LightOj 1203】 凸包 + 边夹角

#include <iostream> #include <cmath> #include <stdio.h> #include <algorithm> #include <vector> using namespace std; const double eps = 1e-18; int sgn(double x) { if(fabs(x) < eps ) return 0; if( x < 0 ) return -1; else return 1; } const int N = 1e5+10; const int maxp = 1e5+10; const double pi = acos(-1); const double inf = 1e14; //square of a double inline double sqr(double x){return x*x;} /* * Point () * distance(Point P) - distance from this to P * len() - get the length from (0,0) to (x,y) * trunc(double r) - transform to the vector which length is r * rad(Point a) */ struct Point { double x,y; double angle; int id; Point(){} /// 无参构造 Point(double _x,double _y){ x = _x; y = _y; } /// 有参构造 Point operator + (const Point b) const{return Point( x+b.x,y+b.y );} Point operator - (const Point b) const{return Point( x-b.x,y-b.y );} /// 向量数乘 Point operator * (const double k) const{ return Point(k*x,k*y); } /// 向量数除 Point operator / (const double k) const{ return Point(x/k , y/k); } bool operator == (const Point b) const{ return sgn(x-b.x) == 0 && sgn(y-b.y) == 0; } bool operator < (const Point b) const { return sgn(x-b.x)==0?sgn(y-b.y)<0:x<b.x; } /// 点积 double operator * (const Point b) const{ return x*b.x + y*b.y; } /// 叉积 double operator ^ (const Point b) const{ return x*b.y - y*b.x; } /// 两点之间的距离 double distance(const Point P) const { return hypot(x-P.x,y-P.y); /// 库函数,求根号下,两数平方和 } /// 向量长度 double len(){ return hypot(x,y); } /// 以 p 为中心点,pa,pb的夹角大小 double rad(Point a,Point b){ Point p = *this; return fabs( atan2( fabs( (a-p)^(b-p) ) , (a-p)*(b-p) ) ); } }; struct Polygon { int n; Point p[maxp]; }; /// 用来指定排序的极点 Point cmpPoint = Point(inf,inf); /// 慎用,第一个基本上就够了 bool cmpCross(const Point a,const Point b) { double d = sgn((a-cmpPoint)^(b-cmpPoint)) ; if( d == 0 ) return sgn( a.distance(cmpPoint) - b.distance(cmpPoint) ) < 0; return d > 0; } ///--------------------- 凸包 --------------------/// /// 求之前的点集 Point convexP[N]; int Sizep; /// 点集大小 void getconvex(Polygon &convex) { /// 按照 x 从小到大排序,如果 x 相同,按 y 排序。 sort(convexP,convexP+Sizep); convex.n = Sizep; for(int i = 0 ; i < min(Sizep,2) ; i ++){ convex.p[i] = convexP[i]; } /// 特判 if( convex.n == 2 && ( convex.p[0] == convex.p[1] ) ) convex.n--; if( Sizep <= 2 ) return ; int &top = convex.n; top = 1; for(int i = 2; i < Sizep ; i++){ while( top && sgn( (convex.p[top] - convexP[i])^(convex.p[top-1]-convexP[i]) ) <= 0 ) top--; convex.p[++top] = convexP[i]; } int temp = top; convex.p[++top] = convexP[Sizep-2]; for(int i = Sizep-3 ; i >= 0 ; i--){ while( top != temp && sgn( (convex.p[top] -convexP[i])^(convex.p[top-1]-convexP[i])) <= 0 ) top --; convex.p[++top] = convexP[i]; } if( convex.n == 2 && ( convex.p[0] == convex.p[1] ) ) convex.n--; } Polygon poly; void solve() { int n; cin >> n; Sizep = n; for(int i = 0; i < n ; i++){ double tex,tey; scanf("%lf%lf",&tex,&tey); convexP[i] = Point(tex,tey); } if( n <= 2 ) {puts("0.000000");return ;} getconvex(poly); double ans_angle = 1e5; for(int i = 0; i < poly.n ; i++){ int j = (i+1)%poly.n; int k = (j+1)%poly.n; // printf("%.2f %.2f %.2f\n",poly.p[j].x,poly.p[i].x,poly.p[k].x); double te_angle = poly.p[j].rad(poly.p[i],poly.p[k]); ans_angle = min(te_angle,ans_angle); } printf("%.6f\n",((ans_angle)*180.0)/pi) ; return ; } int main() { int t; scanf("%d",&t); for(int i = 1; i <= t ; i++){ printf("Case %d: ",i); solve(); } } /* 2 11 6 10 8 2 10 6 10 14 12 4 12 8 16 12 18 10 18 8 18 4 6 8 4 0 0 10 0 10 10 2 1 1 3 0 0 3 0 0 3 */

10、kuangbin 计算几何板子

const double eps = 1e-8; const double pi = acos( -1.0); ///Compares a double to zero int sgn(double x) { if( fabs(x) < eps ) return 0; if( x < 0 ) return -1; else return 1; } ///square of a double inline double sqr(double x) { return x * x; } ///////////////////////////////////////////////// struct Point { double x,y; Point(){} ///no arguments constructor Point(double _x,double _y) { x = _x , y = _y; ///arguments constructor } /*void input(){ scanf("%lf%lf",&x,&y); } void output(){ printf("%.2f %.2f\n",x,y); }*/ bool operator == (Point b) const{ return sgn(x - b.x) == 0 && sgn(y - b.y) == 0; } bool operator < (Point b) const{ return sgn(x - b.x) == 0? sgn(y - b.y) < 0 : x < b.x; } ///数量积 Point operator - (const Point &b) const{ return Point(x - b.x , y - b.y); } Point operator + (const Point &b) const{ return Point(x + b.x , y + b.y); } Point operator * (const double &k) const{ return Point(x * k , y * k ); } Point operator / (const double &k) const{ return Point(x / k , y / k); } ///叉积 double operator ^ (const Point &b) const{ return x * b.y - y * b.x; } ///点积 double operator * (const Point &b) const{ return x * b.x + y * b.y; } ///线段的长度 double len(){ return hypot(x,y); ///<cmath> } ///长度的平方 double len2(){ return x * x + y * y; } ///返回两点的距离 double distance(Point p){ return hypot( x - p.x , y - p.y ); } ///计算 pa 和 pb 的夹角 double rad(Point a,Point b){ Point p = *this; return fabs( atan2( fabs( (a-p)^(b-p) ) , (a-p)*(b-p) ) ); } ///化为长度为r的向量 Point trunc(double r){ double l = len(); if( !sgn(l) ) return *this; r /= l; return Point(-y,x); } ///逆时针旋转90度 Point rotleft(){ return Point(y,-x); } ///顺时针旋转90度 Point rotright(){ return Point(y,-x); } ///绕着p点逆时针 Point rotata(Point p,double angle){ Point v = (*this) - p; double c = cos(angle) , s = sin(angle); return Point(p.x + v.x * c - v.y * s , p.y + v.x *s + v.y * c); } }; struct Line { Point s,e; Line(){} Line( Point _s, Point _e ){ s =_s ; e=_e; } ///由斜倾角angle与任意直线一点确定直线 y = kx + b; void input( Point _s, Point _e ){ s =_s ; e=_e; } Line(Point p,double angle){ s = p; if( sgn(angle - pi/2) == 0 ) e = (s + Point(0,1)); else e = (s + Point(1,tan(angle))); } ///ax + by + c = 0; Line(double a,double b,double c){ if( sgn(a) == 0 ) { s = Point(0,-c/b); e = Point(1,-c/b); } else if(sgn(b) == 0) { s = Point(-c/a,0); e = Point(-c/a,1); } else { s = Point(0,-c/b); e = Point(1,(-c-a)/b); } } double length(){ return s.distance(e);} ///直线与线段相交判断 ///-*this line -v seg ///2规范相交,1非规范相交,0不相交 bool linecrossseg(Line v){ return sgn( (v.s - e) ^ (s - e) ) * sgn(( v.e-e ) ^ (s -e) ) <= 0; } ///点与直线关系 ///1在左侧 ///2在右侧 ///3在直线 int relation(Point p){ int c = sgn( (p-s) ^ (e -s) ); if(c < 0) return 1; else if(c > 0) return 2; else return 3; } ///点在线段上的判断 bool point_on_seg(Point p){ return sgn((p-s)^(e-s) ) == 0 && sgn( (p-s)*(p-e) ) <= 0 ; } ///两向量平行(对应直线平行或重合) bool parallel(Line v){ return sgn( (e-s)^( v.e - v.s ) ) == 0; } ///两直线关系 0-平行,1-重合,2-相交 int linecrossline(Line v){ if( (*this).parallel(v) ) return v.relation(s) == 3; return 2; } ///得到交点,需先判断直线是否相交 Point crosspoint(Line v){ double a1 = ( v.e - v.s ) ^ ( s - v.s ); double a2 = ( v.e - v.s ) ^ ( e - v.s ); return Point( (s.x * a2 - e.x * a1)/(a2 - a1) , (s.y *a2 - e.y *a1)/(a2 - a1)); } ///点到线段的距离 double dispointtoseg(Point p){ if( sgn( (p - s)*(e - s) < 0 ) || sgn( (p-e)*(s-e) ) < 0 ) return min( p.distance(s),p.distance(e) ); return dispointtoline(p); } /// 点到直线的距离 double dispointtoline(Point p){ return fabs( (p-s)^(e-s) ) / length(); } /// 返回点p在直线上的投影 Point lineprog(Point p){ return s + ( ( (e-s)*((e-s)*(p-s)) ) / ( (e-s).len2() ) ); } ///两线段相交判断 ///2 规范相交 ///1 非规范相交 ///0 不想交 int segcrossseg(Line v) { int d1 = sgn((e - s) ^ (v.s - s)); int d2 = sgn((e - s) ^ (v.e - s)); int d3 = sgn((v.e - v.s) ^ (s - v.s)); int d4 = sgn((v.e - v.s) ^ (e - v.s)); if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2)return 2; return (d1 == 0 && sgn((v.s - s) * (v.s - e)) <= 0) || (d2 == 0 && sgn((v.e - s) * (v.e - e)) <= 0) || (d3 == 0 && sgn((s - v.s) * (s - v.e)) <= 0) || (d4 == 0 && sgn((e - v.s) * (e - v.e)) <= 0); } }; struct triangle { Point A,B,C; Line a,b,c; triangle(){} triangle(Point _A,Point _B,Point _C){ A = _A ; B = _B ; C = _C;} ///求重心 Point incenter(){ return Point( ( A.x + B.x + C.x ) / 3, ( A.y + B.y + C.y ) / 3); } }; ///已知三点求圆心与半径模板 void cal(int a,int b,int c)//求外心 ,外心为三角形三边的垂直平分线交点, { double a1 = p[b].x - p[a].x, b1 = p[b].y - p[a].y, c1 = (a1*a1 + b1*b1)/2; double a2 = p[c].x - p[a].x, b2 = p[c].y - p[a].y, c2 = (a2*a2 + b2*b2)/2; double d = a1 * b2 - a2 * b1; x = p[a].x + (c1*b2 - c2*b1)/d,y = p[a].y + (a1*c2 - a2*c1)/d; r = dis2(a); } struct circle{ Point p; ///圆心 double r; ///半径 circle(){} circle( Point _p,double _r ) { p = _p ; r = _r; } bool operator == (circle v){ return (p == v.p) && sgn(r - v.r) == 0; } bool operator < (circle v) const{ return ( (p<v.p) || (p == v.p) && sgn( r - v.r ) < 0 ); } double area(){ return pi*r*r; } double circumference(){ return 2*pi*r; } /// 点与圆的关系 ///0 在圆外 ///1 在圆上 ///2 在圆内 int relation(Point b) { double dst = b.distance(p); if( sgn(dst - r) < 0 ) return 2; else if( sgn(dst-r) == 0 ) return 1; return 0; } ///线段与园的关系 ///比较的是圆心到线段的距离和半径的的关系 int relationseg(Line v){ double dst = v.dispointtoseg(p); if( sgn(dst - r) < 0 ) return 2; else if( sgn(dst - r) == 0 ) return 1; return 0; } /// 直线和圆的关系 /// 比较的是圆心到直线的距离和半径的关系 int relationline(Line v){ double dst = v.dispointtoline(p); if( sgn(dst - r) == 0 ) return 2; else if( sgn( dst - r) == 0) return 1; return 0; } /// 求直线和圆的交点个数 int pointcrossline(Line v,Point &p1,Point &p2){ if( !(*this).relationline(v) ) return 0; Point a = v.lineprog(p); double d = v.dispointtoline(p); d = sqrt(r*r - d*d); if( sgn(d) == 0 ){ p1 = a,p2 = a; return 1; } p1 = a + (v.e - v.s).trunc(d); p2 = a - (v.e - v.s).trunc(d); return 2; } /// 求圆和三角形 pab 的相交面积 double areatriangle( Point a,Point b ){ if( sgn((p-a)^(p-b)) == 0 ) return 0.0; Point q[5]; int len =0; q[len++] = a; Line l(a,b); Point p1,p2; if( pointcrossline( l,q[1],q[2] ) == 2 ){ if( sgn( ( a - q[1] )*( b - q[1] ) ) < 0 ) q[len ++] = q[1]; if( sgn( ( a - q[2] )*( b - q[2] ) ) < 0 ) q[len ++] = q[2]; } q[len ++] = b; if( len == 4 && sgn( (q[0]-q[1])*(q[2]-q[1]) ) > 0 ) swap( q[1],q[2] ); double res = 0; for(int i = 0 ; i < len - 1; i++){ if( relation(q[i]) == 0 || relation( q[i + 1] ) == 0 ){ double arg = p.rad( q[i],q[i + 1] ); res += r*r*arg/2.0; } else{ res += fabs( (q[i] - p) ^ ( q[i+ 1] - p ) ) / 2.0; } } return res; } }; const int maxp = 1100; const int maxl = 2200; struct polygon { int n; ///点的数量 Point p[maxp]; Line l[maxl]; struct cmp{ Point p; cmp(const Point &p0){ p = p0;} bool operator()( const Point &aa ,const Point &bb){ Point a = aa,b = bb; int d = sgn( (a-p)^(b-p) ); if(d == 0) return sgn( a.distance(p) - b.distance(p)) < 0; return d > 0; } }; ///极角排序 ///mi为最左下角的点 void norm(){ Point mi = p[0]; for(int i = 1; i < n; i ++) mi = min(mi,p[i]); sort(p, p + n, cmp(mi) ); } /// 判断任意点与多边形的关系 /// 3在顶点上 /// 2在边上 /// 1在内部 /// 0在外面 int relationpoint(Point tep) { for(int i = 0 ; i < n ; i++){ if( p[i] == tep ) return 3; } for(int i = 0 ; i < n; i++){ if( l[i].point_on_seg(tep) ) return 2; } int tecnt = 0; for(int i = 0 ; i < n ; i++) { int j = (i + 1) % n; int c = sgn( (tep - p[j]) ^ (p[i] - p[j]) ); int u = sgn( p[i].y - tep.y ); int v = sgn( p[j].y - tep.y ); if( c > 0 && u < 0 && v >=0 ) tecnt ++; if( c < 0 && u >= 0 && v < 0 ) tecnt --; } return tecnt != 0; } /// 得到凸包 /// 得到的凸包里的点编号是 0 ~ n-1 的 void getconvex(polygon &convex) { sort(p , p + n); convex.n = n; for(int i = 0 ; i < min(n,2) ; i++){ convex.p[i] = p[i]; } ///特判 if( convex.n == 2 && (convex.p[0] == convex.p[1]) ) convex.n--; if( n <= 2) return; int &top = convex.n; top = 1; for(int i = 2; i < n ; i++){ while(top && sgn( (convex.p[top] - p[i]) ^ (convex.p[top-1] - p[i])) <= 0 ) top --; convex.p[++top] = p[i]; } int temp = top; convex.p[++top] = p[n-2]; for(int i = n - 3; i >=0 ; i--) { while( top!=temp && sgn( (convex.p[top] - p[i]) ^ (convex.p[top-1] - p[i]) ) <=0 ) top--; convex.p[++top] = p[i]; } if( convex.n == 2&& ( convex.p[0] == convex.p[1]) ) convex.n --; ///特判 convex.norm();///得到的是顺时针的点,排序后逆时针 } ///判断是不是凸多边形,用点集,不是得到凸包之后的多边形 bool isconvex(){ bool s[2]; memset(s,false,sizeof(s)); for(int i = 0 ; i < n ; i++){ int j = (i + 1) % n; int k = (j + 1) % n; s[ sgn((p[j] - p[i]) ^ (p[k]-p[i]) ) + 1] =true; if( s[0] && s[2]) return false; } return true; } ///得到周长 double getcircumference(){ double sum = 0; for(int i = 0 ; i < n ; i++){ sum += p[i].distance( p[(i + 1)%n] ); } return sum; } ///得到面积 double getarea() { double sum = 0; for(int i = 0; i < n ; i++){ sum += ( p[i]^p[ (i+1)%n ] ); } return fabs(sum)/2; } ///得到重心 Point getbarycentre(){ Point ret(0,0); double area = 0; for(int i = 1; i < n - 1; i ++){ double tmp = ( p[i] - p[0] ) ^ (p[i + 1] - p[0]); if( sgn(tmp) == 0 ) continue; area += tmp; ret.x += ( p[0].x + p[i].x + p[i + 1].x ) / 3 * tmp; ret.y += ( p[0].y + p[i].y + p[i + 1].y ) / 3 * tmp; } if( sgn(area) ) ret = ret / area; return ret; } ///多边形和园交的面积 double areacircle(circle c){ double ans = 0; for(int i = 0; i < n ; i++) { int j = (i + 1) %n; if( sgn( (p[j] - c.p) ^ ( p[i] - c.p )) >= 0 ) ans += c.areatriangle( p[i],p[j] ); else ans -= c.areatriangle( p[i],p[j] ); } return fabs(ans); } ///多边形和圆的关系 /// 2圆完全在多边形内 /// 1圆在多边形里面,碰到了多边形边界 /// 0其他 int relationcircle(circle c){ int x = 2; if( relationpoint(c.p) != 1 ) return 0; ///圆心不在内部 for(int i = 0; i < n ; i++){ if( c.relationseg(l[i] ) == 2 ) return 0; if( c.relationseg(l[i] ) == 1 ) x = 1; } return x; } };

11、Hoppz 板子

#include <iostream> #include <cmath> #include <stdio.h> #include <algorithm> #include <vector> #include <cstring> using namespace std; const double eps = 1e-18; int sgn(double x) { if(fabs(x) < eps ) return 0; if( x < 0 ) return -1; else return 1; } const int N = 1e5+10; const int maxp = 10; const double pi = acos(-1); const double inf = 1e14; //square of a double inline double sqr(double x){return x*x;} /* * Point () * distance(Point P) - distance from this to P * len() - get the length from (0,0) to (x,y) * trunc(double r) - transform to the vector which length is r * rad(Point a) */ struct Point { double x,y; double angle; int id; Point(){} /// 无参构造 Point(double _x,double _y){ x = _x; y = _y; } /// 有参构造 /// 向量加法 Point operator + (const Point b) const{ return Point( x+b.x,y+b.y ); } /// 向量乘法 Point operator - (const Point b) const{ return Point( x-b.x,y-b.y ); } /// 向量数乘 Point operator * (const double k) const{ return Point(k*x,k*y); } /// 向量数除 Point operator / (const double k) const{ return Point(x/k , y/k); } bool operator == (const Point b) const{ return sgn(x-b.x) == 0 && sgn(y-b.y) == 0; } bool operator < (const Point b) const { return sgn(x-b.x)==0?sgn(y-b.y)<0:x<b.x; } /// 点积 double operator * (const Point b) const{ return x*b.x + y*b.y; } /// 叉积 double operator ^ (const Point b) const{ return x*b.y - y*b.x; } /// 两点之间的距离 double distance(const Point P) const { return hypot(x-P.x,y-P.y); /// 库函数,求根号下,两数平方和 } /// 向量长度 double len(){ return hypot(x,y); } /// 长度的平方,便于之后的计算 double len2(){ return x*x + y*y; } /// 化为长度为 r 的向量 Point trunc(double r){ double l = len(); r /= l; return Point(x*r,y*r); } /// 以 p 为中心点,pa,pb的夹角大小 double rad(Point a,Point b){ Point p = *this; return fabs( atan2( fabs( (a-p)^(b-p) ) , (a-p)*(b-p) ) ); } /// 绕 p点 逆时针选择 angle 度 Point rotate(Point p,double angle){ Point v = (*this) - p; double c = cos(angle) , s = sin(angle); return Point(p.x + v.x * c - v.y * s , p.y + v.x *s + v.y * c); /// 绕原点旋转后,加上原来P点的偏移量 } ///逆时针旋转90度 Point rotleft(){ return Point(y,-x); } ///顺时针旋转90度 Point rotright(){ return Point(y,-x); } }; struct Line { Point s,e; Line(){} Line(Point _s,Point _e){ s =_s; e= _e; } /// 点斜 Line(Point p,double angle){ s = p; if( sgn(angle - pi/2) == 0 ) e = (s + Point(0,1)); else e = (s + Point(1,tan(angle))); } ///ax + by + c = 0; Line(double a,double b,double c){ if( sgn(a) == 0 ) { s = Point(0,-c/b); e = Point(1,-c/b); } else if(sgn(b) == 0) { s = Point(-c/a,0); e = Point(-c/a,1); } else { s = Point(0,-c/b); e = Point(1,(-c-a)/b); } } /// 线段长度 double length(){ return s.distance(e); } /// 返回 0 <= angle <= pi 的基于 x轴 的斜倾角 double angle(){ double k = atan2(e.y-s.y , e.x-s.x); if( sgn(k) < 0 ) k += pi; if( sgn(k-pi) == 0 ) k -=pi; return k; } ///点与直线关系 //1 在左侧 //2 在右侧 //3 在直线 int relation(Point p){ int c = sgn( (p-s) ^ (e -s) ); if(c < 0) return 1; else if(c > 0) return 2; else return 3; } /// 点到直线的距离 double dispointtoline(Point p){ return fabs( (p-s)^(e-s) ) /length(); } /// 返回点p在直线上的投影点(垂足) Point lineprog(Point p){ return s + ( ( (e-s)*((e-s)*(p-s)) ) / ( (e-s).len2() ) ); } /// 返回点p在直线上的对称点 Point symmetrypoint(Point p){ Point q = lineprog(p); return Point(2*q.x-p.x,2*q.y-p.y); } /// 点与线段的位置关系 bool point_on_seg(Point p){ return sgn((p-s)^(e-s) ) == 0 && sgn( (p-s)*(p-e) ) <= 0 ; } /// 点到线段的距离 double dispointtoseg(Point p){ if( sgn((p-s)*(e-s)) < 0 || sgn((p-e)*(s-e))<0 ) return min( p.distance(s),p.distance(e) ); return dispointtoline(p); } /// 判断平行 bool parallel(Line v){ return sgn( (e-s)^(v.e-v.s) ) == 0 ; } /// 判断两直线的位置关系 //0 平行 //1 共线 //2 相交 int linecrossline(Line v){ if( v.parallel(*this) ) return v.relation(s) == 3; return 3; } /// 直线与线段位置关系 -*this line -v seg //2 规范相交 //1 非规范相交(顶点处相交) //0 不相交 int linecrossseg(Line v){ int d1 = sgn( (e-s)^(v.s-s) ); int d2 = sgn( (e-s)^(v.e-s) ); if( (d1 ^ d2) == -2 ) return 2; return (d1==0||d2==0); } ///两线段相交判断 ///2 规范相交 ///1 非规范相交 ///0 不想交 int segcrossseg(Line v) { int d1 = sgn((e - s) ^ (v.s - s)); int d2 = sgn((e - s) ^ (v.e - s)); int d3 = sgn((v.e - v.s) ^ (s - v.s)); int d4 = sgn((v.e - v.s) ^ (e - v.s)); if ((d1 ^ d2) == -2 && (d3 ^ d4) == -2)return 2; return (d1 == 0 && sgn((v.s - s) * (v.s - e)) <= 0) || (d2 == 0 && sgn((v.e - s) * (v.e - e)) <= 0) || (d3 == 0 && sgn((s - v.s) * (s - v.e)) <= 0) || (d4 == 0 && sgn((e - v.s) * (e - v.e)) <= 0); } /// 线段到线段的距离 double disssegtoseg(Line v){ return min( min( dispointtoseg(v.s),dispointtoseg(v.e)) , min(v.dispointtoseg(s), v.dispointtoseg(e) ) ); } /// 求两直线的交点 Point crosspoint(Line v){ double a1 = (v.e-v.s)^(s-v.s); double a2 = (v.e-v.s)^(e-v.s); return Point( (s.x*a2-e.x*a1)/(a2-a1) , (s.y*a2-e.y*a1)/(a2-a1) ); } }; struct Polygon { int n; Point p[maxp]; Line l[maxp]; /// 在多边形中添加点 void add(Point q){ p[n++] = q; } /// 获取所有的线段 void getLine(){ for(int i = 0; i < n ; i++){ l[i] = Line(p[i],p[(i+1)%n]); } } /// 判断多边形是不是凸包 /// 如果是直接对点集效验的话,要先极角排序 bool isconvex(){ bool s[2]; memset(s,0,sizeof s); for(int i = 0 ; i < n ; i++){ int j = (i+1)%n; int k = (j+1)%n; s[ sgn( (p[j]-p[i])^(p[k]-p[i]))+1 ] = true; if( s[0] && s[2] ) return false; } return true; } /// 多边形周长 double getcircumference(){ double sum = 0; for(int i = 0 ; i < n ; i++){ sum += p[i].distance(p[(i+1)%n]); } return sum; } /// 多边形面积 double getarea(){ double sum = 0; for(int i = 0; i < n ; i++){ sum += (p[i]^p[(i+1)%n]); } return fabs(sum)/2; } /// 重心 Point getbarycenter(){ Point ret(0,0); double area = 0 ; for(int i = 1; i < n-1 ; i++){ double tmp = (p[i]-p[0])^(p[i+1]-p[0]); if( sgn(tmp) == 0 ) continue; area += tmp; ret.x += (p[0].x + p[i].x + p[i+1].x)/3*tmp; ret.y += (p[0].y + p[i].y + p[i+1].y)/3*tmp; } if( sgn(area)) ret = ret/area; return ret; } /// 判断点与任意多边形的关系 //3 点上 //2 边上 //1 内部 //0 外部 int relationpoint(Point q){ /// 在点上 for(int i = 0; i < n; i++){ if( p[i] == q ) return 3; } // getLine(); /// 之前getline了的话,可以注释节约时间 /// 在边上 for(int i = 0; i < n ; i++){ if( l[i].point_on_seg(q) ) return 2; } /// 在内部 int cnt = 0; for(int i = 0 ; i < n ; i++){ int j = (i+1)%n; int k = sgn( (q-p[j])^(p[i]-p[j]) ); int u = sgn( p[i].y - q.y ); int v = sgn( p[j].y - q.y ); if( k > 0 && u < 0 && v >= 0 ) cnt ++; if( k < 0 && v < 0 && u >= 0 ) cnt--; } return cnt != 0; } /// 判断多边形是否与多边形相离 int relationpolygon(Polygon Poly){ getLine(); Poly.getLine(); for(int i1 = 0 ; i1 < n ; i1++){ int j1 = (i1+1)%n; for(int i2 = 0 ; i2 <= Poly.n ; i2++){ int j2 = (i2+1)%Poly.n; Line l1 = Line(p[i1],p[j1]); Line l2 = Line(Poly.p[i2],Poly.p[j2]); if( l1.segcrossseg(l2) ) return 0; if( Poly.relationpoint(p[i1]) || relationpoint(Poly.p[i2]) ) return 0; } } return 1; } }; struct Circle { Point p; double r; Circle(){} Circle(Point _p,double _r):p(_p),r(_r){} Circle(double x,double y,double _r){ /// 点的坐标,圆心 p = Point(x,y); r = _r; } bool operator == (Circle v){ return (p == v.p) && sgn(r-v.r) == 0; } /// 以圆心为主排序,半径为副排序 bool operator < (Circle v) const{ return ( (p<v.p) || (p == v.p) && sgn(r-v.r)<0 ); } /// 面积 double area(){ return pi*r*r; } /// 周长 double circumference(){ return 2*pi*r; } /// 点和圆的位置关系 //0 圆外 //1 圆上 //2 圆内 int relationPoint(Point b){ double dst = b.distance(p); if( sgn(dst-r)<0 ) return 2; if( sgn(dst-r)==0 ) return 1; return 0; } /// 直线和圆的关系 //0 相离 //1 相切 //2 相交 int relationLine(Line v){ double dst = v.dispointtoline(p); if( sgn(dst-r) < 0 ) return 2; if( sgn(dst-r) == 0 ) return 1; return 0; } /// 线段和圆的关系 int relationSeg(Line v){ double dst = v.dispointtoseg(p); if( sgn(dst-r) < 0 ) return 2; if( sgn(dst-r) == 0 ) return 1; return 0; } /// 两圆的关系 //5 相离 //4 外切 //3 相交 //2 内切 //1 内含 int relationcircle(Circle v){ double d = p.distance(v.p); if( sgn( d-r-v.r ) > 0 ) return 5; if( sgn( d-r-v.r ) == 0 ) return 4; double l = fabs(r-v.r); if( sgn( d-r-v.r ) < 0 && sgn(d-l) > 0 ) return 3; if( sgn( d-l ) == 0 ) return 2; if( sgn( d-l ) < 0 ) return 1; } }; /// 用来指定排序的极点 Point cmpPoint = Point(inf,inf); /// 需重载 Point 的小于符号 bool cmpAtan2(Point a,Point b) { if( sgn(a.angle - b.angle) == 0 ) return a.distance(cmpPoint) < b.distance(cmpPoint ); return a.angle < b.angle; } /// 慎用,第一个基本上就够了 bool cmpCross(const Point a,const Point b) { double d = sgn((a-cmpPoint)^(b-cmpPoint)) ; if( d == 0 ) return sgn( a.distance(cmpPoint) - b.distance(cmpPoint) ) < 0; return d > 0; } ///--------------------- 凸包 --------------------/// /// 求之前的点集 Point convexP[N]; int Sizep; /// 点集大小 void getconvex(Polygon &convex) { /// 按照 x 从小到大排序,如果 x 相同,按 y 排序。 sort(convexP,convexP+Sizep); convex.n = Sizep; for(int i = 0 ; i < min(Sizep,2) ; i ++){ convex.p[i] = convexP[i]; } /// 特判 if( convex.n == 2 && ( convex.p[0] == convex.p[1] ) ) convex.n--; if( Sizep <= 2 ) return ; int &top = convex.n; top = 1; for(int i = 2; i < Sizep ; i++){ while( top && sgn( (convex.p[top] - convexP[i])^(convex.p[top-1]-convexP[i]) ) <= 0 ) top--; convex.p[++top] = convexP[i]; } int temp = top; convex.p[++top] = convexP[Sizep-2]; for(int i = Sizep-3 ; i >= 0 ; i--){ while( top != temp && sgn( (convex.p[top] -convexP[i])^(convex.p[top-1]-convexP[i])) <= 0 ) top --; convex.p[++top] = convexP[i]; } if( convex.n == 2 && ( convex.p[0] == convex.p[1] ) ) convex.n--; /// 这样求出来的顺时针的凸包,如果要逆时针的话,来一次极角排序就好了。 /// 极角排序 // cmpPoint = convex.p[0]; // sort(convex.p,convex.p+convex.n,cmpCross); }

__EOF__

本文作者Hoppz
本文链接https://www.cnblogs.com/hoppz/p/15619756.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Hoppz  阅读(1315)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示