計算機精度實在不敢恭維,等於號幾乎沒有用,因此要引入一個極小量eps(1e-8什麼的)php
\(a==b \qquad \to \qquad fabs(a-b)<eps\)ios
\(a>b \qquad \to \qquad a-b>eps\)git
\(a<b \qquad \to \qquad a-b<-eps\)算法
不知道點是什麼的能夠走了spa
通常用座標表示3d
struct Point { double x,y; };
沒學過向量的能夠翻翻高中數學或自行百度,不想說了code
咱們也把向量用座標(x,y)表示blog
因此通常和點共用一個結構體排序
若是有強迫症能夠隊列
typedef Point Vector;
模長:就是向量的長度,記爲\(|A|\)
double length(const Vector& v) { return sqrt(v.x*v.x+v.y*v.y); }
<A,B>表示A和B的夾角
計算機算解析式極其繁瑣,因此計算幾何中幾乎全部操做都是用向量實現的
通常用兩個點或一個點和一個向量表示
其實差很少
強烈建議你們學一下重載運算符,不會也不要緊
就是\(B-A=\overrightarrow {AB}\)
Vector operator -(const Point& a,const Point& b) { return (Vector){a.x-b.x,a.y-b.y}; }
Point operator +(const Point& p,const Vector& v) { return (Point){p.x+v.x,p.y+v.y}; }
減法同理
把向量伸長或縮短,或者反向
不要寫成int了
Vector operator *(const Vector& a,const double& k) { return (Vector){a.x*k,a.y*k}; }
和向量加減點同理
是個標量(沒有方向)
double operator ^(const Vector& a,const Vector& b)//用^代替,由於叉積用的多 { return a.x*b.x+a.y*b.y; }
能夠判兩個向量是否垂直
原本是向量,但它垂直於平面,你當標量好了
double operator *(const Vector& a,const Vector& b) { return a.x*b.y-a.y*b.x; }
有向指
這個用途多的多,能夠判斷A和B哪一個在前,A、B是否共線等。
\(|PA|+|PB|=|AB|\)
\(\overrightarrow {PA} \times \overrightarrow {PB} =0\)
\(\overrightarrow {AB} \times \overrightarrow {AC} >0\):逆時針拐彎
\(S=\frac{1}{2} \sum _{i=1}^{n} P_{i} \times P_{i+1}\)
列方程太麻煩,考慮將向量發揚光大
畫出向量
考慮算高
由叉積的幾何意義,兩條粉色虛線之比爲(RE藍×WA紅:AC綠×WA紅)
由類似,等於(無色英雄:P2到交點)
即把原諒綠延長再加上P2
定義:最小的凸多邊形覆蓋全部點
①選出x座標最小的點,若是相同選y座標最小(必定在凸包上),將這個點壓入棧
②全部點按極角(和最開始的點的連線與x軸的夾角)排序
③跑一遍,一直彈棧直到(s[top-1],s[top],當前點)向左拐
複雜度O(nlogn)
容易被卡精度
按x第一關鍵字,y第二關鍵字排,跑完後再倒着跑一遍
精度相對較高
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #define eps 1e-8 using namespace std; struct Point { double x,y; }p[10005]; typedef Point Vector; bool operator < (const Point& a,const Point& b) { return a.x==b.x? a.y<b.y:a.x<b.x; } double operator *(const Vector& a,const Vector& b) { return a.x*b.y-a.y*b.x; } Vector operator -(const Point& a,const Point& b) { return (Vector){a.x-b.x,a.y-b.y}; } double mod(const Vector& v) { return sqrt(v.x*v.x+v.y*v.y); } int n; int st[10005]; int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y); sort(p+1,p+n+1); int m=1; double ans=0.0; st[1]=1; for (int i=2;i<=n;i++) { while (m>1&&(p[st[m]]-p[st[m-1]])*(p[i]-p[st[m-1]])<=0) m--; st[++m]=i; } int k=m; for (int i=n-1;i>=1;i--) { while (m>k&&(p[st[m]]-p[st[m-1]])*(p[i]-p[st[m-1]])<=0) m--; st[++m]=i; } for (int i=1;i<m;i++) { ans+=mod(p[st[i+1]]-p[st[i]]); } printf("%.2lf",ans); return 0; }
關於此算法讀音:一共24種
英文叫Rotating calipers,直譯「旋轉的遊標卡尺」(蛤?)
也就是說卡殼指的是遊標卡尺。因此卡殼是個名詞,那應該讀qiǎ ké
這個算法主要用於算一堆點的最長距離
其實也沒名字說的那麼奧妙
①求出凸包,特判只有2個點的狀況
②假想有一組平行直線把凸包夾着
經過算面積能夠找出離左邊直線也許最遠的點,當它比下一個點長時,就中止,用左邊兩個點到右邊的距離更新答案。
③把直線轉一下,和下一條邊重合,在原來的基礎上把點日後挪,一樣找個貌似是最遠的點
(mspaint不能自定義旋轉,兩個可能長得不同,望理解)
我也不知道爲何反正就是對的
直線最多轉一圈,因此是O(n)的
#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #include <algorithm> #include <stack> #include <cmath> #define MAXN 50005 #define nxt(x) ((x)%top+1) using namespace std; inline int read() { int ans=0,f=1; char c=getchar(); while (!isdigit(c)) { if (c=='-') f=-1; c=getchar(); } while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar(); return f*ans; } struct Point { int x,y; }p[MAXN]; int s[MAXN]; int top=1; typedef Point Vector; bool operator <(const Point& a,const Point& b) { return a.x==b.x? a.y<b.y:a.x<b.x; } int operator *(const Vector& a,const Vector& b) { return a.x*b.y-a.y*b.x; } Vector operator -(const Point& a,const Point& b) { return (Vector){a.x-b.x,a.y-b.y}; } int dis(const Vector& v) { return v.x*v.x+v.y*v.y; } int area(const Point& x,const Point& y,const Point& z) { return (y-x)*(z-x); } int n; void gethull() { sort(p+1,p+n+1); s[1]=1; for (int i=2;i<=n;i++) { while (top>1&&(p[s[top]]-p[s[top-1]])*(p[i]-p[s[top-1]])<=0) top--; s[++top]=i; } int k=top; for (int i=n-1;i>=1;i--) { while (top>k&&(p[s[top]]-p[s[top-1]])*(p[i]-p[s[top-1]])<=0) top--; s[++top]=i; } } int getmax() { int ans=0; if (top==2) return dis(p[s[2]]-p[s[1]]); s[top+1]=s[1]; for (int i=1,j=3;i<=top;i++) { while (nxt(j)!=i&&area(p[s[i]],p[s[i+1]],p[s[j]])<=area(p[s[i]],p[s[i+1]],p[s[j+1]])) j=nxt(j); ans=max(ans,max(dis(p[s[j]]-p[s[i]]),dis(p[s[j]]-p[s[i+1]]))); } return ans; } int main() { n=read(); for (int i=1;i<=n;i++) p[i].x=read(),p[i].y=read(); gethull(); printf("%d",getmax()); return 0; }
問題:給定一些半平面,求它們的交
細節問題:
1.半平面指在給出的直線一邊的平面,具體哪一邊看題。由凸多邊形定義,它必定是凸多邊形
2.交能夠是多邊形,還能夠是直線、射線、線段、點、空集,也能夠不封閉。具體看題。
爲了方便理解,先放一道題
算法流程:
①全部直線按和x軸夾角排序;若是有相同的,只保留最優的(上面的題是更靠「內」的,寫寫就知道了)
②維護一個雙端隊列(由於座標軸上轉了一圈會回來)
跑一遍全部直線
while(當前直線在隊首兩直線交點外)//「外」是指加入這條直線後前兩個半平面的交會變化
彈隊首;
while(當前直線在隊尾兩直線交點外)
彈隊尾;
③清除多餘直線
while(隊尾直線在隊首兩直線交點外)
彈隊首;
while(隊首直線在隊尾兩直線交點外)
彈隊尾;
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define MAXN 505 #define MAXM 55 const double eps=1e-8; using namespace std; struct Point { double x,y; }p[MAXN]; typedef Point Vector; double operator *(const Vector& a,const Vector& b) { return a.x*b.y-a.y*b.x; } Vector operator *(const Vector& a,const double& k) { return (Vector){a.x*k,a.y*k}; } Point operator +(const Point& p,const Vector& v) { return (Point){p.x+v.x,p.y+v.y}; } struct Line { Point a,b; double d; }l[MAXN]; int sta[MAXN]; int top; Vector operator -(const Point& a,const Point& b) { return (Vector){a.x-b.x,a.y-b.y}; } Point inter(const Line& x,const Line& y) { Point p1=x.a,p2=y.a; Vector v1=x.b-p1,v2=y.b-p2,u=p2-p1; Point p=p2+v2*((u*v1)/(v1*v2)); return p; } int sign(const double& a) { if (fabs(a)>eps) return a>0? 1:-1; return 0; } bool operator <(const Line& a,const Line& b) { if (sign(a.d-b.d)==0) return sign((a.b-a.a)*(b.b-a.a))>0; return sign(a.d-b.d)<0; } int n,m; int q[MAXN],head,tail; bool check(const Line& x,const Line& y,const Line& z) { Point p=inter(x,y); return sign((z.b-z.a)*(p-z.a))<0; } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) { int mi; scanf("%d",&mi); for (int j=1;j<=mi;j++) scanf("%lf%lf",&p[j].x,&p[j].y); p[mi+1]=p[1]; for (int j=1;j<=mi;j++) { ++m; l[m].a=p[j]; l[m].b=p[j+1]; } } n=m; for (int i=1;i<=n;i++) l[i].d=atan2(l[i].b.y-l[i].a.y,l[i].b.x-l[i].a.x); sort(l+1,l+n+1); int cnt=0; for (int i=1;i<=n;i++) { if (sign(l[i].d-l[i-1].d)!=0) ++cnt; l[cnt]=l[i]; } head=1,tail=0; q[++tail]=1,q[++tail]=2; for (int i=3;i<=cnt;i++) { while (head<tail&&check(l[q[tail-1]],l[q[tail]],l[i])) --tail; while (head<tail&&check(l[q[head+1]],l[q[head]],l[i])) ++head; q[++tail]=i; } while (head<tail&&check(l[q[tail-1]],l[q[tail]],l[q[head]])) --tail; while (head<tail&&check(l[q[head+1]],l[q[head]],l[q[tail]])) ++head; q[tail+1]=q[head]; n=0; for (int i=head;i<=tail;i++) p[++n]=inter(l[q[i]],l[q[i+1]]); p[n+1]=p[1]; double ans=0; if (n>2) for (int i=1;i<=n;i++) ans+=(p[i]*p[i+1]); ans=fabs(ans)/2.0; printf("%.3f",ans); return 0; }