計算幾何學習筆記

前置

實數

類型:千萬不要用\(float\),用\(double\)
精度\(eps\)通常爲\(1^{-8}\)\(1^{-9}\)
比較:判斷正負:html

int sign(int x){
    return fabs(x)<=eps ? 0 : (x>0 ? 1 : -1);
}

判斷大小:
\(a>b \Rightarrow a-b>0\)
\(a<b \Rightarrow a-b<0\)
\(a==b \Rightarrow a-b==0\)算法

向量

表示\((x,y)\)
運算\((x_1,y_1),(x_2,y_2)\)
\(+\)\((x_1+x_2,y_1+y_2)\)
\(-\)\((x_1-x_2,y_1-y_2)\)
數乘:\((x\ast k,y\ast k)\)
點積:\(x_1\ast x2+y_1\ast y_2\)
叉積:\(x_1\ast y_2-x_2\ast y_1\)spa

其中點積也能夠表示爲:\(|\vec a | |\vec b|\cos \theta\)
叉積爲:\(|\vec a||\vec b|\sin \theta\)
其中\(\theta\)\(\vec a,\vec b\)夾角3d

叉積既有正負,又有大小
正負表示兩個向量的位置關係,
\(\vec a \times \vec b<0\),則a在b的逆時針方向,
\(\vec a \times \vec b>0\),則a在b的順時針方向
\(\vec a \times \vec b=0\),則a、b同向或反向code

大小表示以\(\vec a,\vec b\)爲鄰邊圍成的平行四邊形面積htm

直線

表示:點向法:點+向量
向量垂直:$(x,y)\rightarrow (-y,x)/(y,-x) $blog

判斷點在直線上:叉積爲\(0\)
判斷點在射線上:叉積爲\(0\),點積\(\geq 0\)
判斷點在線段上:叉積爲\(0\)\(x_1\leq x_0 \leq x_2\)&&\(y_1\leq y_0 \leq y_2\)
點到直線距離\(\frac{\vec a\times \vec b}{|\vec a|}\) 如圖:
兩直線交點
\(p_0=p_2+v_2,s_1=\vec u \times \vec v_1,s_2=\vec v_1\times \vec v_2\)
\(k=\frac{s_1}{s_2}\)
\(p_0=p_2+kv_2\)排序

線段相交
\((\vec {AB}\times \vec{AC})\ast (\vec{AB}\times \vec{AD})\leq 0\)&&\((\vec{CD}\times \vec{CA})\ast(\vec{CD}\times \vec{CB})\leq 0\)繼承

凸包

定義

多邊形的內角小於180°get

極角


參照點與選定點的連線與x軸的角度\((0\thicksim 2\pi)\)

極角排序

選定一點,把其它點相對於選定點的極角按大小排序
通常選定左下角的點

Graham算法

先找到左下角的點,其它點排序後,枚舉選出合法的,除去不合法的
用叉積判斷是否合法

模板:

int n;
struct Point{
    int x,y;
    friend Point operator + (Point a,Point b){//加法
        Point t;
        t.x=a.x+b.x;t.y=a.y+b.y;
        return t;
    }
    friend Point operator - (Point a,Point b){//減法
        Point t;
        t.x=a.x-b.x;t.y=a.y-b.y;
        return t;
    }
    friend double operator ^ (Point a,Point b){//叉積
        return a.x*b.y-a.y*b.x;
    }
    friend double operator * (Point a,Point b){//點積
        return a.x*b.x+a.y*b.y;
    }
}a[N],s[N];
int top;

int sign(ORZ x){
    return fabs(x)<=eps ? 0 : (x>0 ? 1 : -1);
}

double dis(Point i,Point j){
    return (i.x-j.x)*(i.x-j.x)+(i.y-j.y)*(i.y-j.y);
}

bool comp(Point i,Point j){
    double x=(i-a[1])^(j-a[1]);//畫圖體驗一下
    return x>0||x==0&&dis(a[1],i)<dis(a[1],j);
}

void Graham(){
    int k=1;
    F(i,2,n) if(a[i].y<a[k].y||(a[i].y==a[k].y&&a[i].x<a[k].x)) k=i;
    swap(a[k],a[1]);
    sort(a+2,a+n+1,comp);
    s[++top]=a[1];s[++top]=a[2];
    F(i,3,n){
        while(top>=2&&sign((s[top]-s[top-1]) ^ (a[i]-s[top-1]))<=0) top--;
        s[++top]=a[i];
    }
}

旋轉卡殼

最遠兩點距離

首先,最遠的兩點必定在凸包上
而且枚舉點時,最遠點是單調的
因此\(O(n)\)求出

邊的最遠點
觀察發現,距離是單調的
能夠枚舉邊,點從上一次繼承過來
能夠用面積表示,當下一個點與線段組成的面積比當前點小,就中止,更新一次答案

代碼:

void work(){
    if(top==2) return dis(s[1],s[2]);
    s[++top]=s[1];
    int j=3;
    F(i,1,top-1){
        while(((s[i+1]-s[i])^(s[j]-s[i])) < ((s[i+1]-s[i])^(s[j+1]-s[i]))){
        //當下一個點與線段組成的面積大於這個點時更新
            j++;
            if(j==top+1) j=1;
        }
        ans=max(ans,max(dis(s[i],s[j]),dis(s[i+1],s[j])));
    }
}

最小矩陣

定義:可以把全部的點都包括的面積最小的矩陣
流程

  1. 求凸包
  2. 枚舉下邊界,去找左右上邊界
  3. 更新答案

    \(i,i+1\)爲下邊界,\(r\)爲最右邊的點
    \(l\)爲最左邊的點,\(p\)爲最上邊的點
    易知,\(r\)爲點積最大的點,\(l\)爲點積最小的點,\(p\)爲叉積最大的點

代碼:

void Min_Mart(){
    int l=1,r=1,p=1;
    double L,R,H;ans=1e50;
    F(i,0,top-1){
        double d=dis(s[i],s[i+1]);
        while((((s[i+1]-s[i])^(s[p+1]-s[i]))-((s[i+1]-s[i])^(s[p]-s[i])))>-eps) (p+=1)%=top;
        //找最遠點
        while(((s[i+1]-s[i])*(s[r+1]-s[i])-(s[i+1]-s[i])*(s[r]-s[i]))>-eps) (r+=1)%=top;
        //找最右邊的點
        if(i==0) l=r;
        while(((s[i+1]-s[i])*(s[l+1]-s[i])-(s[i+1]-s[i])*(s[l]-s[i]))<eps) (l+=1)%=top;
        //找最左邊的點
        L=(s[i+1]-s[i])*(s[l]-s[i])/d;
        R=(s[i+1]-s[i])*(s[r]-s[i])/d;
        H=((s[i+1]-s[i])^(s[p]-s[i]))/d;
        H=fabs(H);
        double sum=fabs(R-L)*H;
        //求面積
        if(sign(sum-ans)<0) {
        //計算點
            ans=sum;
            t[0]=s[i]+(s[i+1]-s[i])*(R/d);
            t[1]=t[0]+(s[r]-t[0])*(H/dis(t[0],s[r]));
            t[2]=t[1]-(t[0]-s[i])*((R-L)/dis(s[i],t[0]));
            t[3]=t[2]-(t[1]-t[0]);
        }
    }
}

半平面交

連接:[CQOI2006]凸多邊形

相關文章
相關標籤/搜索