计算几何总结

基础的东西

一些定义

typedef double db;
struct poi{
    db x,y;
    poi(db X=0,db Y=0) {x=X,y=Y;}
    db len() { return sqrt(x*x+y*y); }
};
poi operator + (poi a,poi b) {return poi(a.x+b.x,a.y+b.y);}
poi operator - (poi a,poi b) {return poi(a.x-b.x,a.y-b.y);}
poi operator * (poi a,db p) {return poi(a.x*p,a.y*p);}
poi operator / (poi a,db p) {return poi(a.x/p,a.y/p);}

判断正负0

//判断正负0
int sign(db x)
{
    if(fabs(x)<eps) return 0;
    if(x>0) return 1;
    return -1;
}

 向量旋转

poi rotate(db c){return (poi){x*cos(c)-y*sin(c),x*sin(c)+y*cos(c)};}
    //逆时针旋转-pi~pi

判断线段和直线是否相交 2984. 线段

//跨立实验:判断一条线段的两端是否在另一条线段的两侧(两个端点与另一线段的叉积乘积为负)。需要正反判断两侧。
int panxdj(poi a,poi b,poi c,poi d)//ab线段,cd直线
{
    if(sign(c.x-d.x)==0&&sign(c.y-d.y)==0) return 0;//直线不能是一个点
    if(sign(cross(a-c,d-c)*cross(b-c,d-c))>0) return 0;
    return 1;
}

判断点在直线上

int cxd(poi a,poi b,poi p)
//p-a x p-b ==0 && p-a * p-b <=0
{
    if(sign(cross(p-a,p-b))==0&&sign(dot(p-a,p-b))<=0) return 1;
    return 0;
}

射线法判断点在多边形内

db dbarea()
{
    db res=0;
    for(int i=1;i<=n;i++)
    res+=cross(a[i],a[i+1]);
    return res;
}

int cdb(poi p)
{
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(cxd(a[i],a[i+1],p)) return 1;
        int d1=sign(a[i].y-p.y),d2=sign(a[i+1].y-p.y);
        int det=sign(cross(a[i]-p,a[i+1]-p));//cross
        if((det>=0&&d1<0&&d2>=0)||(det<=0&&d1>=0&&d2<0)) cnt++;
    }
    return cnt&1;
}


for(int i=1;i<=n;i++)
scanf("%lf %lf",&a[i].x,&a[i].y);
a[n+1]=a[1];
if(sign(dbarea())<0)
{
    reverse(a+1,a+n+1);
    a[n+1]=a[1];
}

判断两直线关系 

先判重合,再判平行,再求交点

db cross(poi a,poi b)
{
    return a.x*b.y-b.x*a.y;
}
poi zhixianjd(poi a,poi b,poi c,poi d)
{
    poi p;
    db s1=cross(c-a,d-a),s2=cross(d-b,c-b);//s1 ac*ad s2 bd*bc
    p=s1/(s1+s2)*(b-a)+a;
    return p;
}
int pandianxian(poi a,poi b,poi p)
{
    if(sign(cross(p-a,p-b))==0) return 1;
    return 0;
}
if(pandianxian(a,b,c)&&pandianxian(a,b,d))
{
    cout<<"LINE"<<endl;
    continue;
}
if(sign(cross(b-a,d-c))==0)
{
    cout<<"NONE"<<endl;
    continue;
}
poi p=zhixianjd(a,b,c,d);

 求凸包

poi dian[N];
int cmp(poi l,poi r)
{
    int det=sing(cross(l-dian[1],r-dian[1]));
    if(det) return det>0;
    return (l-dian[1]).len()<(r-dian[1]).len();
}
int n,m;
poi p[N];
void graham()
{
    int k=1;
    for(int i=2;i<=n;i++)
    if(dian[i].x<dian[k].x||(sing(dian[i].x-dian[k].x)==0&&dian[i].y<dian[k].y)) k=i;
    if(k^1) swap(dian[1],dian[k]);
    sort(dian+2,dian+n+1,cmp);
    //for(int i=1;i<=n;i++) cout<<dian[i].x<<" "<<dian[i].y<<endl;
    m=1;
    p[1]=dian[1];
    for(int i=2;i<=n;i++)
    {
        while(m-1&&sing(cross(p[m]-dian[i],p[m-1]-p[m]))>=0) m--;
        p[++m]=dian[i];
    }
}

半平面交

int l=1,r=1;
int cmp(qw l,qw r)
{
    db t1=atan2(l.b.y-l.a.y,l.b.x-l.a.x),t2=atan2(r.b.y-r.a.y,r.b.x-r.a.x);
    if(sing(t1-t2)!=0) return t1<t2;
    return sing(cross(l.b-l.a,r.a-l.a))<0;//r.a
}
db ang(qw xian)
{
    return atan2(xian.b.y-xian.a.y,xian.b.x-xian.a.x);
}
void bpingj()
{
    sort(line+1,line+n+1,cmp);
    q[1]=line[1];
    for(int i=2;i<=n;i++)
    {
        poi a=line[i].a,b=line[i].b;
        if(sing(ang(line[i])-ang(line[i-1]))==0) continue;
        while(l<r&&sing(cross(b-a,jd[r]-a))<=0) r--;
        while(l<r&&sing(cross(b-a,jd[l+1]-a))<=0) l++;
        q[++r]=line[i];
        if(l<r) jd[r]=zhixianjd(q[r-1].a,q[r-1].b,a,b);
    }
    while(l<r&&sing(cross(q[l].b-q[l].a,jd[r]-q[l].a))<=0) r--;
    jd[r+1]=zhixianjd(q[l].a,q[l].b,q[r].a,q[r].b);
    r++;
}

最小圆覆盖

cir get_cir(poi i,poi j,poi k)
{
    poi p1,p2;
    p1=(i+j)/2+(j-i).rotate(pi/2);
    p2=(i+k)/2+(k-i).rotate(pi/2);
    poi jd=zhixianjd((i+j)/2,p1,(i+k)/2,p2);
    cir c;
    c.a=jd;
    c.r=(jd-i).len();//i
    return c;
}
    
random_shuffle(dian+1,dian+n+1);
cir c=(cir){dian[1],0};
for(int i=2;i<=n;i++)
if(sing((dian[i]-c.a).len()-c.r)==1)
{
    c=(cir){dian[i],0};
    for(int j=1;j<i;j++)
    if(sing((dian[j]-c.a).len()-c.r)==1)
    {
        c=(cir){(dian[i]+dian[j])/2,(dian[i]-dian[j]).len()/2};
        for(int k=1;k<j;k++)
        if(sing((dian[k]-c.a).len()-c.r)==1)
        {
             c=get_cir(dian[i],dian[j],dian[k]);
        }
    }
}

三维凸包

#include<bits/stdc++.h>
using namespace std;
const int N=405;
typedef double db;
const db eps=1e-12;
struct poi{
    db x,y,z;
    poi(db X=0,db Y=0,db Z=0){x=X,y=Y,z=Z;}
    db len(){return sqrt(x*x+y*y+z*z);}
    db rand_eps(){return ((db)rand()/RAND_MAX-0.5)*eps;}
    void shake() { x+=rand_eps(),y+=rand_eps(),z+=rand_eps(); }
}dian[N];
poi operator + (poi a,poi b){return (poi){a.x+b.x,a.y+b.y,a.z+b.z};}
poi operator - (poi a,poi b){return (poi){a.x-b.x,a.y-b.y,a.z-b.z};}
db dot(poi a,poi b)
{
    return a.x*b.x+a.y*b.y+a.z*b.z;
}
poi cross(poi a,poi b)
{
    return (poi){a.y*b.z-b.y*a.z,a.z*b.x-b.z*a.x,a.x*b.y-b.x*a.y};
}
struct plane{
    int v[3];
    poi a,b,c;
    poi normal()//求法向量
    {
        return cross(c-a,b-a);
    }
    db area()//求面积
    {
        return normal().len()/2;
    }
    int above(poi p)
    {
        return dot(p-a,normal())>=0;
    }
}pl[N],lb[N];
bool g[N][N];
int n,m;
void get_tubao3()
{
    pl[++m]=(plane){1,2,3,dian[1],dian[2],dian[3]};
    pl[++m]=(plane){3,2,1,dian[3],dian[2],dian[1]};
    for(int i=4;i<=n;i++)
    {
        int cnt=0;
        for(int j=1;j<=m;j++)
        {
            int t=pl[j].above(dian[i]);
            if(!t) lb[++cnt]=pl[j];
            for(int k=0;k<3;k++)
            g[pl[j].v[k]][pl[j].v[(k+1)%3]]=t;
        }
        for(int j=1;j<=m;j++)
        for(int k=0;k<3;k++)//不要写xxx+xxx==1 这样的话面就重复了,一开始加两个一样的面是为了让4一定有面做底,另一个面和4一起组成另外三个面
        if(g[pl[j].v[k]][pl[j].v[(k+1)%3]]&&!g[pl[j].v[(k+1)%3]][pl[j].v[k]])
            lb[++cnt]={pl[j].v[k],pl[j].v[(k+1)%3],i,dian[pl[j].v[k]],dian[pl[j].v[(k+1)%3]],dian[i]};
        m=cnt;
        for(int j=1;j<=cnt;j++) pl[j]=lb[j];
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    scanf("%lf %lf %lf",&dian[i].x,&dian[i].y,&dian[i].z);
    for(int i=1;i<=n;i++) dian[i].shake();
    get_tubao3();
    db res=0;
    for(int i=1;i<=m;i++) res+=pl[i].area();
    printf("%.6lf",res);
    return 0;
}

旋转卡壳

    db res=0;
    for(int i=1,j=3;i<=m;i++)
    {
        poi a=p[i],b=p[i%m+1];
        while(sing(fabs(cross(a-b,a-p[j%m+1]))-fabs(cross(a-b,a-p[j])))>0) j=j%m+1;
        res=max(res,(p[j]-a).len());
        res=max(res,(p[j]-b).len());
    }

扫描线

区间合并

        ll num=0;
        int st=la[1].x,ed=la[1].y;
        for(int j=2;j<=cnt;j++)
        if(la[j].x<=ed) ed=max(ed,la[j].y);
        else res+=1ll*(ed-st)*(r-l+1),st=la[j].x,ed=la[j].y;
        num+=1ll*(ed-st)*(r-l+1);

自适应辛普森积分

db spi(db a,db b)
{
    return (f(a)+4*f((a+b)/2)+f(b))*(b-a)/6;
}
db xps(db a,db b)
{
    db s=spi(a,b),l=spi(a,(a+b)/2),r=spi((a+b)/2,b);
    if(fabs(s-l-r)<=eps) return s;
    return xps(a,(a+b)/2)+xps((a+b)/2,b);
}

f是自己定义的函数

原文地址:https://www.cnblogs.com/xsm098/p/14819143.html