两个吃奶一个添下面视频_人妻第一页香蕉网_欧美xxxx少妇_妺妺窝人体色www婷婷

什么是“二維計(jì)算幾何”?當(dāng)我們將需要解決的幾何問題的范圍限制在二維平面內(nèi),這樣就用到了二維計(jì)算幾何。要用電腦解平面幾何題?這就是陷入誤區(qū)了,我們并不是用計(jì)算機(jī)算數(shù)學(xué)卷子上的幾何題去了,而是解決一些更加復(fù)雜的幾何相關(guān)問題。

為了解決復(fù)雜且抽象的問題,我們一定要選擇合適的研究方法。對(duì)于計(jì)算機(jī)來說,給它看幾何圖形……

我們可以把要研究的圖形放在平面直角坐標(biāo)系或極坐標(biāo)系下,這樣解決問題就會(huì)方便很多。

計(jì)算幾何中坐標(biāo)一般是實(shí)數(shù),編程時(shí)使用double,不要使用精度較低的float。

在進(jìn)行浮點(diǎn)數(shù)運(yùn)算時(shí)會(huì)產(chǎn)生精度誤差,可以設(shè)置一個(gè)偏差值eps(epsilon)來控制精度。

判斷浮點(diǎn)數(shù)是否等于零或兩個(gè)浮點(diǎn)數(shù)是否相等要用eps輔助判斷。

#include<bits/stdc++.h>
using namespace std;
#define db double
const db pi = acos(-1.0);   //高精度圓周率
const db eps = 1e-8;        //可根據(jù)具體需要更改精度
inline int sgn(db x){		//判斷是否等于零
	if(fabs(x)<eps) return 0;
	else return x<0?-1:1;
}
inline int dcmp(db x,db y){	//比較浮點(diǎn)數(shù)
	if(fabs(x-y)<eps)return 0;
	else return x<y?-1:1;
}

點(diǎn)和向量

1. 點(diǎn)

二位平面中的點(diǎn)用(x,y)表示。

struct Point{
	db x,y;
	Point(){}
	Point(db _x,db _y):x(_x),y(_y){}
};

2. 兩點(diǎn)之間的距離

db Dist(Point &a,Point &b){
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

3. 向量

有大小,有方向的量稱為向量(矢量) ,只有大小沒有方向的量稱為標(biāo)量。

在平面上兩個(gè)點(diǎn)可確定一個(gè)向量,由于向量不是一個(gè)有向線段,及向量沒有起點(diǎn)與終點(diǎn)的概念,所有向量平移后不變。大多時(shí)候?yàn)榱朔奖憧芍苯訉⑾蛄科揭频皆c(diǎn)處。向量的表示在形式上與點(diǎn)一樣,可以用點(diǎn)的數(shù)據(jù)結(jié)構(gòu)表示向量。

typedef Point Vector;

4. 向量的運(yùn)算

struct Point中,對(duì)向量運(yùn)算重載運(yùn)算符。

+:點(diǎn)加點(diǎn)沒有意義,點(diǎn)加向量得到另一個(gè)點(diǎn),向量加向量得到另一個(gè)向量。

Point operator+(Point &a){return Point(x+a.x,y+a.y);}

-:兩個(gè)點(diǎn)的差得到一個(gè)向量,向量A減B得到有B指向A的向量。

Point operator-(Point &a){return Point(x-a.x,y-a.y);}

向量的運(yùn)算

*:向量乘實(shí)數(shù)等比例放大。

Point operator*(db a){return Point(x*a,y*a);}

/:向量與實(shí)數(shù)相除等比例縮小。

Point operator/(db a){return Point(x/a,y/a);}

==:相等

bool operator==(Point &a){return sgn(x-a.x)==0&&sgn(y-a.y)==0;}


點(diǎn)積和叉積

向量的基本運(yùn)算是點(diǎn)積和叉積,計(jì)算幾何的各種操作幾乎都基于這兩種操作。

1. 點(diǎn)積(Dot product)

點(diǎn)積

其中θ為A,B之間的夾角。點(diǎn)積的幾何意義是A在B上投影的長度乘以B的模長。

在編程中并不需要知道θ。如果已知A=(A.x,A.y),B=(B.x,B.y),那么有:

A?B=A.x?B.x+A.y?B.y

A.x?B.x+A.y?B.y

=(|A|cosθ1?|B|cosθ2)+(|A|sinθ1?|B|sinθ2)

|A||B|(cosθ1cosθ2+sinθ1sinθ2)

|A||B|cos(θ1?θ2)

|A||B|cosθ

db Dot(Vector &a,Vector &b){return a.x*b.x+a.y*b.y;}

2. 點(diǎn)積的應(yīng)用

(1)判斷A,B之間的角是鈍角銳角還是直角。

(2)求A的長度(模)

db len(Vector &a){return sqrt(Dot(a,a));}

(3)求A,B夾角的大小

db Angle(Vector &a,Vector &b){return acos(Dot(a,b)/len(a)/len(b));}

3. 叉積(Cross product)

叉積是比點(diǎn)積等常用的集合概念。

叉積

θ表示A旋轉(zhuǎn)到B所經(jīng)過的夾角。

兩個(gè)向量的叉積的結(jié)果是一個(gè)帶符號(hào)的數(shù)值,其幾何意義為A,B形成平行四邊形的“有向”面積,其正負(fù)可通過“右手定則”判斷。

db Cross(Vector &a,Vector &b){return a.x*b.y-a.y*b.x;}

A.x?B.y?A.y?B.x

=|A||B|(cosθ1sinθ2?sinθ1cosθ2)

=|A||B|sin(θ2?θ1)

當(dāng)θ2>θ1時(shí),結(jié)果為正,且B在A的逆時(shí)針方向。

故可通過正負(fù)來判斷A,B的相對(duì)位置。

4. 叉積的基本應(yīng)用

(1)判斷A,B的方向關(guān)系。

(2)計(jì)算兩向量構(gòu)成的平行四邊形的“有向面積”

db Area2(Point a,Point b,Point c){return Cross(b-a,c-a);}

(3)計(jì)算3個(gè)點(diǎn)構(gòu)成的三角形面積

(4)向量旋轉(zhuǎn)

是向量(x,y)繞起點(diǎn)逆時(shí)針旋轉(zhuǎn)θ,旋轉(zhuǎn)后的向量為(x′,y′)

向量旋轉(zhuǎn)

Vector Rotate(Vector a,db rad){
	return Vector(a.x*cos(rad)-a.y*sin(rad),a.x*sin(rad)+a.y*cos(rad));
}

(5)用叉積檢查向量是否平行或重合

bool Parallel(Vector a,Vector b){return sgn(Cross(a,b))==0;}


點(diǎn)和線

1. 直線的表示

直線的表示方法很多,編程時(shí)可靈活選擇:

(1)用直線上的兩個(gè)點(diǎn)表示。

(2)ax+by+c=0,普通式。

(3)y=kx+b,斜街式,注意斜率不存在的情況。

(4)P=P0+vt,點(diǎn)向式。用P0和v來表示直線P,t是任意值,P0是直線上一點(diǎn),v是方向向量,v=A?B。

t無限制時(shí),P是直線。

t∈[0,1],P是A,B之間的線段。

t>0,P是射線。

struct line{
	Point p1,p2;
	line(){}
	line(Point _p1,Point _p2):p1(_p1),p2(_p2){}
	//一個(gè)點(diǎn)和角(angle)確定直線,0<=angle<pi
	line(Point p,db angle){
		p1=p;
		if(sgn(angle-pi/2)==0){p1=p+Point(0,1);}
		else p1=p+Point(1,tan(angle));
	}
	//ax+by+c=0;
	line(db a,db b,db c){
		if(sgn(a)==0){
			p1=Point(0,-c/b);
			p2=Point(1,-c/b);
		}
		else if(sgn(b)==0){
			p1=Point(-c/a,0);
			p2=Point(-c/a,1);
		}
		else{
			p1=Point(0,-c/b);
			p2=Point(-c/a,0);
		}
	}
};

2. 線段的表示

可以用兩個(gè)點(diǎn)表示線段,直接用直線的數(shù)據(jù)結(jié)構(gòu)表示線段。

typedef line Segment;

3. 在二維平面上,點(diǎn)和直線有三種位置關(guān)系,在直線上,左側(cè)和右側(cè)。用叉積的正負(fù)可判斷方向。

int Point_line_relation(Point p,line v){
	int c=sgn(Cross(p-v.p1,v.p2-v.p1));
	if(c<0)return 1;		//順時(shí)針
	else if(c>0)return 2;	        //逆時(shí)針
	return 0;			//在直線上
}

4. 點(diǎn)和線段的關(guān)系

先用叉積判斷是否共線,再用點(diǎn)積判斷點(diǎn)與線段兩端的角是否為180°。

int Point_on_seg(Point p,line v){
	return sgn(Cross(v.p1-p,v.p2-p))==0&&sgn(Dot(v.p1-p,v.p2-p))<=0;
}

5. 點(diǎn)到直線的距離

已知點(diǎn)p到直線v(p1,p2),求p到v的距離。首先用叉積求p,p1,p2構(gòu)成的平行四邊形的面積,然后除以線段(p1,p2)的長度即可。

db Dis_point_line(Point p,line v){
    return fabs(Cross(p-v.p1,v.p2-v.p1)/Dist(v.p1,v.p2));//注意叉積有正負(fù)
}

6. 點(diǎn)在直線上的投影

點(diǎn)在直線上的投影

已知直線上的兩點(diǎn)p1,p2以及直線外一點(diǎn)p,求投影點(diǎn)p0,我們先來看下上圖左邊的那種情形。

點(diǎn)在直線上的投影1,即k是線段p0p1和p2p1長度的比值,那么有點(diǎn)在直線上的投影2。

根據(jù)點(diǎn)積的概念有,

點(diǎn)在直線上的投影3

點(diǎn)在直線上的投影4

帶入即:點(diǎn)在直線上的投影5

那么:點(diǎn)在直線上的投影6

容易證明上圖右邊那種情況也滿足此式。

Point Point_line_proj(Point p,line v){
	double k=Dot(p-v.p1,v.p2-p)/len(v.p2-v.p1)/len(v.p2-v.p1);
	return v.p1+(v.p2-v.p1)*k;
}

7. 點(diǎn)關(guān)于直線的對(duì)稱點(diǎn)

點(diǎn)關(guān)于直線的對(duì)稱點(diǎn)

求點(diǎn)p關(guān)于直線的對(duì)稱點(diǎn),先求投影點(diǎn),在求對(duì)稱點(diǎn)。

Point Point_line_symmetry(Point p,line v){
	Point p1=Point_line_proj(p,v);
	return Point(p1.x*2-p.x,p1.y*2-p.y);
}

8. 點(diǎn)到線段的距離

點(diǎn)p到線段AB的距離,在p到A的距離,p到B的距離,p到直線AB的距離(如果垂足在線段AB上)。

db Dis_point_seg(Point p,Segment v){
	if(Dot(p-v.p1,v.p2-v.p1)<0||Dot(p-v.p2,v.p1-v.p2)<0)return min(Dist(p,v.p1),Dist(p,v.p2));
	return Dis_point_line(p,v);
}

9. 兩條直線的位置關(guān)系

int line_relation(line v1,line v2){
	if(sgn(Cross(v1.p1-v1.p2,v2.p1-v2.p2))==0){
		if(Point_line_relation(v1.p1,v2)==0)return 1;//重合
		else return 0;//平行
	}
	return 2;//相交
}

10. 求兩個(gè)直線的交點(diǎn)

可以聯(lián)立方程求解,也可以用簡單的叉積來實(shí)現(xiàn)。

求兩個(gè)直線的交點(diǎn)

如圖,AB與CD的交點(diǎn)為P。

容易得到以下兩個(gè)等式:

兩個(gè)等式

聯(lián)立上面兩個(gè)方程,即可得到點(diǎn)P的坐標(biāo):

得到點(diǎn)P的坐標(biāo)

Point Cross_point(line a,line b){
	db s1=Cross(b.p1-a.p1,a.p2-a.p1);
	db s2=Cross(a.p2-a.p1,b.p2-a.p1);//注意叉積的正負(fù)
	return Point(s1*b.p2.x+s2*b.p1.x,s1*b.p2.y+s2*b.p1.y)/(s1*s2);
}

注意函數(shù)要對(duì)(s1+s2)做除法,在調(diào)用前要保證兩直線不平行且不重合。

11. 判斷兩線段是否有交點(diǎn)

bool Cross_segment(Segment a,Segment b){
	db c1=Cross(a.p1-a.p2,b.p1-a.p2),c2=Cross(a.p1-a.p2,b.p2-a.p2);
	db d1=Cross(b.p1-b.p2,a.p1-b.p2),d2=Cross(b.p1-b.p2,a.p2-b.p2);
	return c1*c2<=0&&d1*d2<=0;
}

12. 求兩個(gè)線段的交點(diǎn)

先判斷有沒有交點(diǎn),有交點(diǎn)轉(zhuǎn)化為直線求就行了。


多邊形

1. 判斷點(diǎn)在多邊形內(nèi)部

給定一個(gè)點(diǎn)P和一個(gè)多邊形,判斷點(diǎn)P在多邊形的內(nèi)部還是外部。

從點(diǎn)P引出一條直線(與多邊形交于兩點(diǎn)),檢查P與多邊形每條邊的相交情況,檢查的方向不影響結(jié)果,但不能中途改變方向。

檢查以下3個(gè)參數(shù):

0001.png

判斷點(diǎn)在多邊形內(nèi)部

顯然u,v是用來判斷是否相交,c用來判斷p與邊的位置關(guān)系。

最后當(dāng)num>0時(shí)p在多邊形內(nèi)部。

int Point_in_polygon(Point pt,Point *p,int n){
	//注意P[]中的點(diǎn)為嚴(yán)格逆時(shí)針或順時(shí)針
	for(int i=0;i<n;++i){
		if(p[i]==pt)return 3;//點(diǎn)p在多邊形的頂點(diǎn)上
	}
	for(int i=0;i<n;++i){
		line v=line(p[i],p[(i+1)%n]);
		if(Point_on_seg(pt,v))return 2;//點(diǎn)在多邊形邊上
	}
	int num=0;
	for(int i=0;i<n;++i){
		int j=(i+1)%n;
		int c=sgn(Cross(pt-p[j],p[i]-p[j]));
		int u=sgn(p[i].y-pt.y);
		int v=sgn(p[j].y-pt.y);
		if(c>0&&u<0&&v>=0)num++;
		if(c<0&&u>=0&&v<0)num--;//注意這里不能用n*v<0判斷
	}
	return num>0;//1在內(nèi)部,0在外部 
}

3. 求多邊形面積

求多邊形面積

對(duì)于一個(gè)多邊形,可以在其內(nèi)部找一點(diǎn)p,將多邊形劃分為n個(gè)三角形,計(jì)算n個(gè)三角形的面積之和即可。

實(shí)際上p的位置可任意選取,叉積的正負(fù)可將多余的面積抵消掉,所以一般將p選在原點(diǎn)。

db Polygon_area(Point*p,int n){
	db ans=0;
	for(int i=0;i<n;++i){
		ans+=Cross(p[i],p[(i+1)/n]);
	}
	return ans/2;//注意面積有正負(fù),可根據(jù)需要判斷是否要去絕對(duì)值
}
點(diǎn)贊(1)

C語言網(wǎng)提供由在職研發(fā)工程師或ACM藍(lán)橋杯競賽優(yōu)秀選手錄制的視頻教程,并配有習(xí)題和答疑,點(diǎn)擊了解:

一點(diǎn)編程也不會(huì)寫的:零基礎(chǔ)C語言學(xué)練課程

解決困擾你多年的C語言疑難雜癥特性的C語言進(jìn)階課程

從零到寫出一個(gè)爬蟲的Python編程課程

只會(huì)語法寫不出代碼?手把手帶你寫100個(gè)編程真題的編程百練課程

信息學(xué)奧賽或C++選手的 必學(xué)C++課程

藍(lán)橋杯ACM、信息學(xué)奧賽的必學(xué)課程:算法競賽課入門課程

手把手講解近五年真題的藍(lán)橋杯輔導(dǎo)課程

Dotcpp在線編譯      (登錄可減少運(yùn)行等待時(shí)間)