知识点 - 几何模板 解析几何版

本文深入探讨了计算几何中直线与线段交点的解析几何方法,包括直线交点的求解公式、线段交点的判断算法及线性变换原理。同时,详细介绍了圆线交、圆圆交及圆圆切线的计算方法,提供了丰富的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

知识点 - 计算几何方程(解析几何)版

直线交,线段交

Ax+By+C=0.A x + B y + C = 0.Ax+By+C=0.可以由两个点的得到Px,Py,Qx,QyP_x , P_y , Q_x , Q_yPx,Py,Qx,Qy .
A=Py−Qy,B=Qx−Px,C=−APx−BPy. A = P_y - Q_y,B = Q_x - P_x,C = - A P_x - B P_y. A=PyQy,B=QxPx,C=APxBPy.
联立方程
{a1x+b1y+c1=0a2x+b2y+c2=0 \begin{cases} a_1 x + b_1 y + c_1 = 0 \\\\ a_2 x + b_2 y + c_2 = 0 \end{cases}\\ a1x+b1y+c1=0a2x+b2y+c2=0
解得
x=−∣c1b1c2b2∣∣a1b1a2b2∣=−c1b2−c2b1a1b2−a2b1,y=−∣a1c1a2c2∣∣a1b1a2b2∣=−a1c2−a2c1a1b2−a2b1. x = - \frac{\begin{vmatrix}c_1 & b_1 \cr c_2 & b_2\end{vmatrix}}{\begin{vmatrix}a_1 & b_1 \cr a_2 & b_2\end{vmatrix} } = - \frac{c_1 b_2 - c_2 b_1}{a_1 b_2 - a_2 b_1},\\ y = - \frac{\begin{vmatrix}a_1 & c_1 \cr a_2 & c_2\end{vmatrix}}{\begin{vmatrix}a_1 & b_1 \cr a_2 & b_2\end{vmatrix}} = - \frac{a_1 c_2 - a_2 c_1}{a_1 b_2 - a_2 b_1}. x=a1a2b1b2c1c2b1b2=a1b2a2b1c1b2c2b1,y=a1a2b1b2a1a2c1c2=a1b2a2b1a1c2a2c1.

struct pt {
    double x, y;

    bool operator<(const pt& p) const
    {
        return x < p.x - EPS || (abs(x - p.x) < EPS && y < p.y - EPS);
    }
};

struct line {
    double a, b, c;

    line() {}
    line(pt p, pt q)
    {
        a = p.y - q.y;
        b = q.x - p.x;
        c = -a * p.x - b * p.y;
        norm();
    }

    void norm()
    {
        double z = sqrt(a * a + b * b);
        if (abs(z) > EPS)
            a /= z, b /= z, c /= z;
    }

    double dist(pt p) const { return a * p.x + b * p.y + c; }
};

const double EPS = 1e-9;

double det(double a, double b, double c, double d) {
    return a*d - b*c;
}
//直线交
bool intersect(line m, line n, pt & res) {
    double zn = det(m.a, m.b, n.a, n.b);
    if (abs(zn) < EPS)
        return false;
    res.x = -det(m.c, m.b, n.c, n.b) / zn;
    res.y = -det(m.a, m.c, n.a, n.c) / zn;
    return true;
}

bool parallel(line m, line n) {
    return abs(det(m.a, m.b, n.a, n.b)) < EPS;
}

bool equivalent(line m, line n) {
    return abs(det(m.a, m.b, n.a, n.b)) < EPS
        && abs(det(m.a, m.c, n.a, n.c)) < EPS
        && abs(det(m.b, m.c, n.b, n.c)) < EPS;
}

//线段交
inline bool betw(double l, double r, double x)
{
    return min(l, r) <= x + EPS && x <= max(l, r) + EPS;
}

inline bool intersect_1d(double a, double b, double c, double d)
{
    if (a > b)
        swap(a, b);
    if (c > d)
        swap(c, d);
    return max(a, c) <= min(b, d) + EPS;
}

bool intersect(pt a, pt b, pt c, pt d, pt& left, pt& right)
{
    if (!intersect_1d(a.x, b.x, c.x, d.x) || !intersect_1d(a.y, b.y, c.y, d.y))
        return false;
    line m(a, b);
    line n(c, d);
    double zn = det(m.a, m.b, n.a, n.b);
    if (abs(zn) < EPS) {
        if (abs(m.dist(c)) > EPS || abs(n.dist(a)) > EPS)
            return false;
        if (b < a)
            swap(a, b);
        if (d < c)
            swap(c, d);
        left = max(a, c);
        right = min(b, d);
        return true;
    } else {
        left.x = right.x = -det(m.c, m.b, n.c, n.b) / zn;
        left.y = right.y = -det(m.a, m.c, n.a, n.c) / zn;
        return betw(a.x, b.x, left.x) && betw(a.y, b.y, left.y) &&
               betw(c.x, d.x, left.x) && betw(c.y, d.y, left.y);
    }
}

附带判断是否线段相交,用叉乘版

struct pt {
    long long x, y;
    pt() {}
    pt(long long _x, long long _y) : x(_x), y(_y) {}
    pt operator-(const pt& p) const { return pt(x - p.x, y - p.y); }
    long long cross(const pt& p) const { return x * p.y - y * p.x; }
    long long cross(const pt& a, const pt& b) const { return (a - *this).cross(b - *this); }
};

int sgn(const long long& x) { return x >= 0 ? x ? 1 : 0 : -1; }

bool inter1(long long a, long long b, long long c, long long d) {
    if (a > b)
        swap(a, b);
    if (c > d)
        swap(c, d);
    return max(a, c) <= min(b, d);
}

bool check_inter(const pt& a, const pt& b, const pt& c, const pt& d) {
    if (c.cross(a, d) == 0 && c.cross(b, d) == 0)
        return inter1(a.x, b.x, c.x, d.x) && inter1(a.y, b.y, c.y, d.y);
    return sgn(a.cross(b, c)) != sgn(a.cross(b, d)) &&
           sgn(c.cross(d, a)) != sgn(c.cross(d, b));
}

线性变换

如何将一个点(x1,y1)变换(旋转,平移,反射,投影,伸缩,切变)到另一个点(x2,y2)?你首先要知道3个点变换前后的坐标,然后列方程:
(x1,y1)−&gt;(x2,y2)x2=A1⋅x1+B1⋅y1+C1y2=A2⋅x1+B2⋅y1+C2 (x1,y1)-&gt;(x2,y2)\\ x2=A1\cdot x1+B1\cdot y1+C1\\ y2=A2\cdot x1+B2\cdot y1+C2 (x1,y1)>(x2,y2)x2=A1x1+B1y1+C1y2=A2x1+B2y1+C2

六个方程即可解出
高斯消元,用分数保证精度

圆线交

不妨设圆心在原点,(若不是这样,可以移动坐标系)
∵d0=∣C∣A2+B2∴x0=−ACA2+B2y0=−BCA2+B2又∵d=r2−C2A2+B2,令m=d2A2+B2∴ax=x0+B⋅m,ay=y0−A⋅mbx=x0−B⋅m,by=y0+A⋅m \because d_0 = \frac{|C|}{\sqrt{A^2+B^2}} \\ \therefore x_0 = - \frac{AC}{A^2 + B^2} \\ y_0 = - \frac{BC}{A^2 + B^2} \\又\because d = \sqrt{r^2 - \frac{C^2}{A^2 + B^2}} ,令 m = \sqrt{\frac{d^2}{A^2 + B^2}} \\ \therefore a_x = x_0 + B \cdot m, a_y = y_0 - A \cdot m\\ b_x = x_0 - B \cdot m, b_y = y_0 + A \cdot m d0=A2+B2Cx0=A2+B2ACy0=A2+B2BCd=r2A2+B2C2,m=A2+B2d2ax=x0+Bm,ay=y0Ambx=x0Bm,by=y0+Am

double r, a, b, c; // given as input
double x0 = -a*c/(a*a+b*b), y0 = -b*c/(a*a+b*b);
if (c*c > r*r*(a*a+b*b)+EPS)
    puts ("no points");
else if (abs (c*c - r*r*(a*a+b*b)) < EPS) {
    puts ("1 point");
    cout << x0 << ' ' << y0 << '\n';
}
else {
    double d = r*r - c*c/(a*a+b*b);
    double mult = sqrt (d / (a*a+b*b));
    double ax, ay, bx, by;
    ax = x0 + b * mult;
    bx = x0 - b * mult;
    ay = y0 - a * mult;
    by = y0 + a * mult;
    puts ("2 points");
    cout << ax << ' ' << ay << '\n' << bx << ' ' << by << '\n';
}

圆圆交

不妨设一个圆心在原点
x2+y2=r12(x−x2)2+(y−y2)2=r22 x^2+y^2=r_1^2\\ (x - x_2)^2 + (y - y_2)^2 = r_2^2 x2+y2=r12(xx2)2+(yy2)2=r22
连立得到
x⋅(−2x2)+y⋅(−2y2)+(x22+y22+r12−r22)=0 x \cdot (-2x_2) + y \cdot (-2y_2) + (x_2^2+y_2^2+r_1^2-r_2^2) = 0 x(2x2)+y(2y2)+(x22+y22+r12r22)=0
变成了圆线交。

圆圆切线

不妨设一个圆心在原点。问题转化为一条直线到v1v_1v1距离r1r_1r1,v2v_2v2距离r2r_2r2.另外我们让A2+B2=1A^2+B^2=1A2+B2=1这样保证直线只有一种形式,并且化简了点到直线公式。于是得到:
a2+b2=1∣a⋅0+b⋅0+c∣=r1∣a⋅vx+b⋅vy+c∣=r2 a^2 + b^2 = 1\\\mid a \cdot 0 + b \cdot 0 + c \mid = r_1\\\mid a \cdot v_x + b \cdot v_y + c \mid = r_2 a2+b2=1a0+b0+c=r1avx+bvy+c=r2
拆绝对值,令d1=±r1,d2=±r2d_1 = \pm r_1,d_2 = \pm r_2d1=±r1,d2=±r2
a=(d2−d1)vx±vyvx2+vy2−(d2−d1)2vx2+vy2b=(d2−d1)vy±vxvx2+vy2−(d2−d1)2vx2+vy2c=d1 a = {( d_2 - d_1 ) v_x \pm v_y \sqrt{v_x^2 + v_y^2-(d_2-d_1)^2} \over {v_x^2 + v_y^2} }\\ b = {( d_2 - d_1 ) v_y \pm v_x \sqrt{v_x^2 + v_y^2-(d_2-d_1)^2} \over {v_x^2 + v_y^2} }\\ c = d_1 a=vx2+vy2(d2d1)vx±vyvx2+vy2(d2d1)2b=vx2+vy2(d2d1)vy±vxvx2+vy2(d2d1)2c=d1
移回去的方法是c−=a⋅x0+b⋅y0c-= a \cdot x_0 + b \cdot y_0c=ax0+by0

double sqr (double a) {
    return a * a;
}
void tangents (pt c, double r1, double r2, vector<line> & ans) {
    double r = r2 - r1;
    double z = sqr(c.x) + sqr(c.y);
    double d = z - sqr(r);
    if (d < -EPS)  return;
    d = sqrt (abs (d));
    line l;
    l.a = (c.x * r + c.y * d) / z;
    l.b = (c.y * r - c.x * d) / z;
    l.c = r1;
    ans.push_back (l);
}

vector<line> tangents (circle a, circle b) {
    vector<line> ans;
    for (int i=-1; i<=1; i+=2)
        for (int j=-1; j<=1; j+=2)
            tangents (b-a, a.r*i, b.r*j, ans);
    for (size_t i=0; i<ans.size(); ++i)
        ans[i].c -= ans[i].a * a.x + ans[i].b * a.y;
    return ans;
}

线段并

统计直线上的线段并总长

int length_union(const vector<pair<int, int>> &a) {
    int n = a.size();
    vector<pair<int, bool>> x(n*2);
    for (int i = 0; i < n; i++) {
        x[i*2] = {a[i].first, false};
        x[i*2+1] = {a[i].second, true};
    }

    sort(x.begin(), x.end());

    int result = 0;
    int c = 0;
    for (int i = 0; i < n * 2; i++) {
        if (i > 0 && x[i].first > x[i-1].first && c > 0)
            result += x[i].first - x[i-1].first;
        if (x[i].second)
            c--;
        else
            c++;
    }
    return result;
}

复杂度:

例题

代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Best KeyBoard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值