首页 > 代码库 > 计算几何学习1

计算几何学习1

目前在跟着这个题目列表

来学习, 目前进行了一中的大部分,模板是参考唐天晓学长的板子和白书来搞的。

学习的内容:

1.复数类的一些常用操作

技术分享
typedef complex <double> Point;// 复数类来简化代码
Point a;
a.real(), a.imag();// a的实部与虚部 对应二维平面 x,y 
abs(a);// 向量a的模/a到原点距离 
norm(a);// 模的平方 
arg(a);// a对应的角度 (弧度制 
conj(a);// a的共轭 (来计算点积与叉积
polar(r, theta);// 返回极坐标 (r, theta) 的 Point型 
View Code

2.关于点/向量的一些操作

技术分享
double Det(const Point & a, const Point & b){//叉积 
    return (conj(a) * b).imag();
}

double Dot(const Point & a, const Point & b){//点积 
    return (conj(a) * b).real();
}

int sgn(double x) {//浮点数的正负比较 
    if(abs(x) < eps) return 0;
    if(x < 0) return -1;
    return 1;
}

Point Rotate(const Point & o , double theta){//仿射变换 将一个点绕原点 逆时针旋转theta 
    return (Point) {o.real() * cos(theta) - o.imag() * sin (theta), o.real() * sin(theta) + o.imag() * cos(theta)};
}

Point ConvertPoint(const Point & u ,const Point & v, const Point & o){//坐标变换 
    // A * u + B * v = o 不成立的情况下 
    //求o在 u ,v 为基底的情况下的坐标 
    return (Point) {Det(o, v) / Det(u, v), Det(o, u) / Det(v, u)};
}
View Code

3.关于线的一些操作

技术分享
struct Line : public vector <Point>{//存储直线上的两点 或表示线段 
    Line(){};
    Line(const Point & a, const Point & b){
        push_back(a);
        push_back(b);
        return;
    }
};

Point Vec(const Point & a){//单位向量 
    return a / abs(a);
}

Point Vec(const Line & a) {//方向向量 
    return a[1] - a[0];
}

Point LineIntersection(const Line & a, const Line & b){//直线的交 
    Point u = a[0] - b[0];
    double k1 = Det(Vec(a), Vec(b));
    double k2 = Det(Vec(b), u);
    if(!sgn(k1))//当方向向量共线时 返回 a的端点 外面判断是否平行 
        return a[0];
    return a[0] + Vec(a) * k2 / k1;
}

bool SegmentIntersection(const Line & a, const Line & b){ //两条线段是否存在交点 
    if(max(a[0].real(), a[1].real()) < min(b[0].real(), b[1].real())) return 0; 
    if(max(b[0].real(), b[1].real()) < min(a[0].real(), a[1].real())) return 0;
    if(max(a[0].imag(), a[1].imag()) < min(b[0].imag(), b[1].imag())) return 0;
    if(max(b[0].imag(), b[1].imag()) < min(a[0].imag(), a[1].imag())) return 0;
    Point t1 = Vec(a), t2 = Vec(b);
    return (sgn(Det(a[0] - b[0], t2)) * sgn(Det(a[1] - b[0], t2)) <= 0) && (sgn(Det(b[0] - a[0], t1)) * sgn(Det(b[1] - a[0], t1)) <= 0); 
}

int LSFLAG = 0;//用于判断交点是否存在 是否在线段上 
Point LSIntersection(const Line & a, const Line & b){//线段与直线的交 a为线段 b为直线 
    Point c = LineIntersection (a, b);
    LSFLAG = 0;
    if(sgn(Det(a[0] - b[0], Vec(b))) * sgn(Det(a[1] - b[0], Vec(b))) > 0 ){
        LSFLAG = -1;
        return c;
    }
    if(!sgn(Det(Vec(a), Vec(b)))) LSFLAG = -1;//平行 或  重合 交点无意义 
    else{
        if(sgn(Dot(a[0] - c, a[1] - c)) <= 0) return c;//交点在线段上 
        else LSFLAG = -1;//交点不在线段上 
    }
    return c;
}

Point CrossVP(const Line & v, const Point & u){//找点到直线距离(最近点 返回垂足 
    if(!sgn(v[0].real() - v[1].real()) && !sgn(v[0].imag() - v[1].imag())) return v[0];
    if(sgn(Dot(Vec(v), u - v[0])) < 0) return v[0];//删掉两行是直线 一行不删是线段 
    if(sgn(Dot(Vec(v), u - v[1])) > 0) return v[1];//删掉这行是射线
    double a = Dot(Vec(v), u - v[0]);
    return v[0] + a / norm(Vec(v)) * Vec(v);
}
View Code

 

一些题目和注意事项:

模板题:

poj 1269 直线交

poj 2653 线段交

poj 2624 裸向量  *注意题目中输入时点的顺序可能不确定

poj 1569 判断点是否在在凸多边形内部 凸多边形面积求法 *注意在边界的情况有一部分叉积为0 *多边形叉积出来面积有向 比较大小时abs

 

不是很模板的题:

poj 3304

转化问题:是否存在一条直线与所有给定线段相交

假如存在一条这样直线 我们一定可以通过平移或旋转一定角度 来使这条直线恰好通过 某些线段的两个端点

因此枚举端点 + 直线与线段的交

*枚举不同端点时 注意判断重复的点

 

poj 1039

光线由一头通过一条无反射,无折射的管道中最多能传播多远(最远x坐标

跟上面的问题类似

一条可行直线可以通过平移 旋转使其贴到上下边界各一个端点

而通过的部分一定会和端点处垂直的线段相交(不规范相交 端点处可以通过)

因此

1)枚举上下端点来确定直线, 判断是否能与起始的垂直线段相交(首先要从起始线段射出光线

2)如果能通过所有拐角处垂直线段 一定会通过整条管道

3) 在做不到2)的情况下 找到最靠前的无法相交的垂线段 之前的上下边界处可更新答案

*注意计算几何题目中给出的数据范围 这道题点的坐标没有给范围 起始可能为负

poj 1556

线段交 + 最短路

*RE的可能情况

1)除0

2)数组越界 下标计算出了问题 或者 数组本身不够大

3)  死循环

 

明天的目标:

考虑到训练赛和可能的补题,尽量完成提到的blog中的第一部分的剩余内容

以及kuangbin的练习题的剩余部分

尽量在周三开始凸包及之后内容的学习

 

计算几何学习1