计算几何笔记

 直线与线段模板

#include <bits/stdc++.h>
#define int long long
#define mp make_pair
#define pb push_back
#define all(a) a.begin(), a.end()
#define rep(i, a, n) for (int i = a; i < n; i++)
#define per(i, a, n) for (int i = n - 1; i >= a; i--)
#define eps 1e-8
#define zero(x) (((x) > 0 ? (x) : -(x)) < eps)
using namespace std;
struct point
{
    double x, y;
};
struct line
{
    point a, b;
};
//计算 cross product (P1-P0)x(P2-P0)
double xmult(point p1, point p2, point p0)
{
    return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}
double xmult(double x1, double y1, double x2, double y2, double x0, double y0)
{
    return (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0);
}
//计算 dot product (P1-P0).(P2-P0)
double dmult(point p1, point p2, point p0)
{
    return (p1.x - p0.x) * (p2.x - p0.x) + (p1.y - p0.y) * (p2.y - p0.y);
}
double dmult(double x1, double y1, double x2, double y2, double x0, double y0)
{
    return (x1 - x0) * (x2 - x0) + (y1 - y0) * (y2 - y0);
}
//两点距离
double distance(point p1, point p2)
{
    return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
double distance(double x1, double y1, double x2, double y2)
{
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
//判三点是否共线
int dots_inline(point p1, point p2, point p3)
{
    return zero(xmult(p1, p2, p3));
}
// 判点是否在线段上
//判点是否在线段上,包括端点(下面为两种接口模式)
int dot_online_in(point p, line l)
{
    return zero(xmult(p, l.a, l.b)) && (l.a.x - p.x) * (l.b.x - p.x) < eps && (l.a.y - p.y) * (l.b.y - p.y) < eps;
}
int dot_online_in(point p, point l1, point l2)
{
    return zero(xmult(p, l1, l2)) && (l1.x - p.x) * (l2.x - p.x) < eps && (l1.y - p.y) * (l2.y - p.y) < eps;
}
//判点是否在线段上,不包括端点
int dot_online_ex(point p, line l)
{
    return dot_online_in(p, l) && (!zero(p.x - l.a.x) || !zero(p.y - l.a.y)) && (!zero(p.x - l.b.x) || !zero(p.y - l.b.y));
}
//判断两点在线段的同一侧
//判两点在线段同侧,点在线段上返回 0
int same_side(point p1, point p2, line l)
{
    return xmult(l.a, p1, l.b) * xmult(l.a, p2, l.b) > eps;
}
int same_side(point p1, point p2, point l1, point l2)
{
    return xmult(l1, p1, l2) * xmult(l1, p2, l2) > eps;
}
//判断两点是否在线段的异侧
//判两点在线段异侧,点在线段上返回 0
int opposite_side(point p1, point p2, line l)
{
    return xmult(l.a, p1, l.b) * xmult(l.a, p2, l.b) < -eps;
}
int opposite_side(point p1, point p2, point l1, point l2)
{
    return xmult(l1, p1, l2) * xmult(l1, p2, l2) < -eps;
}
//求点关于直线的对称点
// 点关于直线的对称点 // by lyt
// 缺点:用了斜率
// 也可以利用"点到直线上的最近点"来做,避免使用斜率。
point symmetric_point(point p1, point l1, point l2)
{
    point ret;
    if (l1.x > l2.x - eps && l1.x < l2.x + eps)
    {
        ret.x = (2 * l1.x - p1.x);
        ret.y = p1.y;
    }
    else
    {
        double k = (l1.y - l2.y) / (l1.x - l2.x);
        ret.x = (2 * k * k * l1.x + 2 * k * p1.y - 2 * k * l1.y - k * k * p1.x + p1.x) / (1 + k * k);
        ret.y = p1.y - (ret.x - p1.x) / k;
    }
    return ret;
}
//判断两线段是否相交
//常用版
//定义点
struct Point
{
    double x;
    double y;
};
typedef struct Point point;
//叉积
double multi(point p0, point p1, point p2)
{
    return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}

//相交返回 true,否则为 false, 接口为两线段的端点
bool isIntersected(point s1, point e1, point s2, point e2)
{
    return (max(s1.x, e1.x) >= min(s2.x, e2.x)) &&
           (max(s2.x, e2.x) >= min(s1.x, e1.x)) &&
           (max(s1.y, e1.y) >= min(s2.y, e2.y)) &&
           (max(s2.y, e2.y) >= min(s1.y, e1.y)) &&
           (multi(s1, s2, e1) * multi(s1, e1, e2) > 0) &&
           (multi(s2, s1, e2) * multi(s2, e2, e1) > 0);
}
//不常用版
//判两线段相交,包括端点和部分重合
int intersect_in(line u, line v)
{
    if (!dots_inline(u.a, u.b, v.a) || !dots_inline(u.a, u.b, v.b))
        return !same_side(u.a, u.b, v) && !same_side(v.a, v.b, u);
    return dot_online_in(u.a, v) || dot_online_in(u.b, v) || dot_online_in(v.a, u) || dot_online_in(v.b, u);
}
int intersect_in(point u1, point u2, point v1, point v2)
{
    if (!dots_inline(u1, u2, v1) || !dots_inline(u1, u2, v2))
        return !same_side(u1, u2, v1, v2) && !same_side(v1, v2, u1, u2);
    return dot_online_in(u1, v1, v2) || dot_online_in(u2, v1, v2) || dot_online_in(v1, u1, u2) || dot_online_in(v2, u1, u2);
}
//判两线段相交,不包括端点和部分重合
int intersect_ex(line u, line v)
{
    return opposite_side(u.a, u.b, v) && opposite_side(v.a, v.b, u);
}
int intersect_ex(point u1, point u2, point v1, point v2)
{
    return opposite_side(u1, u2, v1, v2) && opposite_side(v1, v2, u1, u2);
}
//求两条直线的交点
//计算两直线交点,注意事先判断直线是否平行!
//线段交点请另外判线段相交(同时还是要判断是否平行!)
point intersection(point u1, point u2, point v1, point v2)
{
    point ret = u1;
    double t = ((u1.x - v1.x) * (v1.y - v2.y) - (u1.y - v1.y) * (v1.x - v2.x)) / ((u1.x - u2.x) * (v1.y - v2.y) - (u1.y - u2.y) * (v1.x - v2.x));
    ret.x += (u2.x - u1.x) * t;
    ret.y += (u2.y - u1.y) * t;
    return ret;
}
//点到直线的最近距离
point ptoline(point p, point l1, point l2)
{
    point t = p;
    t.x += l1.y - l2.y, t.y += l2.x - l1.x;
    return intersection(p, t, l1, l2);
}
//点到线段的最近距离
point ptoseg(point p, point l1, point l2)
{
    point t = p;
    t.x += l1.y - l2.y, t.y += l2.x - l1.x;
    if (xmult(l1, t, p) * xmult(l2, t, p) > eps)
        return distance(p, l1) < distance(p, l2) ? l1 : l2;
    return intersection(p, t, l1, l2);
}

 凸包面积

#include <bits/stdc++.h>
#define int long long
#define mp make_pair
#define pb push_back
#define all(a) a.begin(), a.end()
#define rep(i, a, n) for (int i = a; i < n; i++)
#define per(i, a, n) for (int i = n - 1; i >= a; i--)
#define eps 1e-8
#define zero(x) (((x) > 0 ? (x) : -(x)) < eps)
using namespace std;
const int maxn = 1e5 + 10;
struct point
{
    double x, y;
} p[maxn], P[maxn];

double X(point a, point b, point c)
{
    return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
}

double len(point a, point b)
{
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

bool cmp(point a, point b)
{
    if (X(p[0], a, b) > 0)
        return true;
    else if (X(p[0], a, b) < 0)
        return false;
    else
        return len(p[0], a) < len(p[0], b);
}

signed main()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; i++)
        scanf("%lf%lf", &p[i].x, &p[i].y);
    for (int i = 0; i < n; i++)
    {
        if (p[i].y < p[0].y || p[i].y == p[0].y && p[i].x < p[0].x)
            swap(p[i], p[0]);
    }

    sort(p + 1, p + n, cmp);
    P[0] = p[0];
    P[1] = p[1];
    int tot = 1;
    for (int i = 2; i < n; i++)
    {
        while (tot > 0 && X(P[tot - 1], P[tot], p[i]) <= 0)
            tot--;
        P[++tot] = p[i];
    }
    double s = 0;
    for (int i = 0; i <= tot; i++)
    {
        printf("%lf %lf\n", P[i].x, P[i].y);
    }
    for (int i = 2; i <= tot; i++)
    {
        s += X(P[0], P[i - 1], P[i]);
    }
    s /= 2;
    printf("%.2lf\n", s);
    return 0;
}

半平面交

#include <bits/stdc++.h>
#define int long long
#define mp make_pair
#define pb push_back
#define all(a) a.begin(), a.end()
#define rep(i, a, n) for (int i = a; i < n; i++)
#define per(i, a, n) for (int i = n - 1; i >= a; i--)
#define eps 1e-8
#define zero(x) (((x) > 0 ? (x) : -(x)) < eps)
using namespace std;
const int maxn = 200;

struct Point
{ //点的表示
    double x, y;
    Point(double _x = 0, double _y = 0)
    {
        x = _x;
        y = _y;
    }
    friend Point operator+(const Point &a, const Point &b)
    {
        return Point(a.x + b.x, a.y + b.y);
    }
    friend Point operator-(const Point &a, const Point &b)
    {
        return Point(a.x - b.x, a.y - b.y);
    }
    friend Point operator==(const Point &a, const Point &b)
    {
        return fabs(a.x - b.x) < eps && fabs(a.y - b.y) < eps;
    }
} convex[maxn];
struct V
{ //向量的表示
    Point start, end;
    double ang; //角度,[-180,180]
    V(Point _start = Point(0, 0), Point _end = Point(0, 0), double _ang = 0)
    {
        start = _start;
        end = _end;
        ang = _ang;
    }
    friend V operator+(const V &a, const V &b)
    {
        return V(a.start + b.start, a.end + b.end);
    }
    friend V operator-(const V &a, const V &b)
    {
        return V(a.start - b.start, a.end - b.end);
    }
} l[maxn], st[maxn];
struct Triangle
{
    Point A, B, C;
};
int n, cnt;

double DotMul(V a, V b)
{ //点积
    a.end = a.end - a.start;
    b.end = b.end - b.start;
    return a.end.x * b.end.x + a.end.y * b.end.y;
}
double CroMul(V a, V b)
{ //叉积 a×b
    a.end = a.end - a.start;
    b.end = b.end - b.start;
    return a.end.x * b.end.y - b.end.x * a.end.y;
}
int IsLineInter(V l1, V l2)
{ //相交
    if (max(l1.start.x, l1.end.x) >= min(l2.start.x, l2.end.x) &&
        max(l2.start.x, l2.end.x) >= min(l1.start.x, l1.end.x) &&
        max(l1.start.y, l1.end.y) >= min(l2.start.y, l2.end.y) &&
        max(l2.start.y, l2.end.y) >= min(l1.start.y, l1.end.y))
    {
        if (CroMul(l2, V(l2.start, l1.start)) * CroMul(l2, V(l2.start, l1.end)) <= 0 &&
            CroMul(l1, V(l1.start, l2.start)) * CroMul(l1, V(l1.start, l2.end)) <= 0)
        {
            return 1;
        }
    }
    return 0;
}
Point LineInterDot(V l1, V l2)
{ //交点
    Point p;
    double S1 = CroMul(V(l1.start, l2.end), V(l1.start, l2.start));
    double S2 = CroMul(V(l1.end, l2.start), V(l1.end, l2.end));
    p.x = (l1.start.x * S2 + l1.end.x * S1) / (S1 + S2);
    p.y = (l1.start.y * S2 + l1.end.y * S1) / (S1 + S2);
    return p;
}
int JudgeOut(const V &x, const Point &p)
{                                          //点在线的左侧
    return CroMul(V(x.start, p), x) > eps; //点在左侧返回0,右侧返回1
}
int Parellel(const V &x, const V &y)
{ //平行
    return fabs(CroMul(x, y)) < eps;
}
void ChangeDirection()
{
    for (int i = 0; i < n; ++i)
    {
        swap(l[i].start, l[i].end);
    }
}
double CheckDirection()
{
    double ans = 0;
    for (int i = 0; i < n; ++i)
    { //判断是否是顺时针
        ans += CroMul(V(Point(0, 0), l[i].start), V(Point(0, 0), l[i].end));
    }
    return ans; // ans>0逆时针,sum<0顺时针
}
int Cmp(V a, V b)
{
    if (fabs(a.ang - b.ang) < eps)
    { //角度相同时,不同的边在不同的位置
        //此时有两种return方式,
        // return CorMul(a,V(b.end-a.start))>=0;
        //左边的边在后面的位置,这样的话,进行计算的时候就可以忽略 相同角度边的影响了
        return CroMul(V(b.end - a.start), V(a.end - b.start)) > eps;
        //左边的边在前面的位置,要进行进行去重判断 。
    }
    return a.ang < b.ang;
}
double HplaneIntersection()
{
    int top = 1, bot = 0;
    sort(l, l + n, Cmp);
    int tmp = 1;
    for (int i = 1; i < n; ++i)
    {
        if (l[i].ang - l[i - 1].ang > eps)
        { //去重,如果该边和前面的边平行,则忽略。
            l[tmp++] = l[i];
        }
    }
    n = tmp;
    st[0] = l[0];
    st[1] = l[1];
    for (int i = 2; i < n; ++i)
    {
        if (Parellel(st[top], st[top - 1]) || Parellel(st[bot], st[bot + 1]))
            return 0;
        while (bot < top && JudgeOut(l[i], LineInterDot(st[top], st[top - 1])))
            --top;
        while (bot < top && JudgeOut(l[i], LineInterDot(st[bot], st[bot + 1])))
            ++bot;
        st[++top] = l[i];
    }
    //	while(bot<top&&(Parellel(st[top],st[top-1])||JudgeOut(st[bot],LineInterDot(st[top],st[top-1])))) --top;
    //	while(bot<top&&(Parellel(st[bot],st[bot+1])||JudgeOut(st[top],LineInterDot(st[bot],st[bot+1])))) ++bot;
    while (bot < top && JudgeOut(st[bot], LineInterDot(st[top], st[top - 1])))
        --top;
    while (bot < top && JudgeOut(st[top], LineInterDot(st[bot], st[bot + 1])))
        ++bot;
    if (top <= bot + 1)
        return 0.00;
    st[++top] = st[bot];
    cnt = 0;
    for (int i = bot; i < top; ++i)
    {
        convex[cnt++] = LineInterDot(st[i], st[i + 1]);
    }
    double ans = 0;
    convex[cnt] = convex[0];
    for (int i = 0; i < cnt; ++i)
    {
        ans += CroMul(V(Point(0, 0), convex[i]), V(Point(0, 0), convex[i + 1]));
    }
    return ans / 2;
}
signed main()
{
    int t;
    cin >> t;
    while (t--)
    {
        scanf("%lld", &n);
        if (n == 0)
            break;
        scanf("%lf%lf", &l[0].end.x, &l[0].end.y);
        l[n - 1].start.x = l[0].end.x;
        l[n - 1].start.y = l[0].end.y;
        for (int i = 1; i < n; ++i)
        {
            scanf("%lf%lf", &l[i].end.x, &l[i].end.y);
            l[i - 1].start.x = l[i].end.x;
            l[i - 1].start.y = l[i].end.y;
        }
        for (int i = 0; i < n; ++i)
        {
            l[i].ang = atan2(l[i].end.y - l[i].start.y, l[i].end.x - l[i].start.x);
        }
        if (CheckDirection() < 0)
            ChangeDirection(); //改成逆时针
        //默认从点在线的左边。
        double ans = HplaneIntersection();
        printf("%.3lf\n", ans);
    }
}

平面最近点对

#include <bits/stdc++.h>
#define int long long
#define mp make_pair
#define pb push_back
#define all(a) a.begin(), a.end()
#define rep(i, a, n) for (int i = a; i < n; i++)
#define per(i, a, n) for (int i = n - 1; i >= a; i--)
#define eps 1e-8
#define zero(x) (((x) > 0 ? (x) : -(x)) < eps)
using namespace std;
const int maxn = 1e6 + 10;
const double INF = 1e20;

struct Point
{
	double x;
	double y;
} point[maxn];
int n;
int tmpt[maxn];

bool cmpxy(const Point &a, const Point &b)
{
	if (a.x != b.x)
		return a.x < b.x;
	return a.y < b.y;
}

bool cmpy(const int &a, const int &b)
{
	return point[a].y < point[b].y;
}

double min(double a, double b)
{
	return a < b ? a : b;
}

double dis(int i, int j)
{
	return sqrt((point[i].x - point[j].x) * (point[i].x - point[j].x) + (point[i].y - point[j].y) * (point[i].y - point[j].y));
}

double Closest_Pair(int left, int right)
{
	double d = INF;
	if (left == right)
		return d;
	if (left + 1 == right)
		return dis(left, right);
	int mid = (left + right) >> 1;
	double d1 = Closest_Pair(left, mid);
	double d2 = Closest_Pair(mid + 1, right);
	d = min(d1, d2);
	int i, j, k = 0;
	//分离出宽度为d的区间
	for (i = left; i <= right; i++)
	{
		if (fabs(point[mid].x - point[i].x) <= d)
			tmpt[k++] = i;
	}
	sort(tmpt, tmpt + k, cmpy);
	//线性扫描
	for (i = 0; i < k; i++)
	{
		for (j = i + 1; j < k && point[tmpt[j]].y - point[tmpt[i]].y < d; j++)
		{
			double d3 = dis(tmpt[i], tmpt[j]);
			if (d > d3)
				d = d3;
		}
	}
	return d;
}
signed main()
{
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
		scanf("%lf %lf", &point[i].x, &point[i].y);
	sort(point, point + n, cmpxy);
	printf("%.4lf\n", Closest_Pair(0, n - 1));
	return 0;
}

pick定理:

  • pick定理:给定顶点均为整点的简单多边形,皮克定理说明了其面积A和内部格点数目 i,边上给点数目 b 的关系

  • A = i + \frac{b}{2}-1
  • 拓展:在一个格点坐标系中,已知线段两端点的坐标分别为(x1,y1),(x2,y2)。则该线段经过的格点数为:gcd(|x1-x2|,|y1-y2|)+1

  • 特别的,当x1=x2,经过的结点数为|y1-y2|,反之。

两直线相交

直线一般式:

L1:A_{1} + B_{1}y +c_{1} = 0

L2:A_{2} + B_{2}y +c_{2} = 0

直线点斜式:

\left\{\begin{matrix}x=(b_{2}-b_{1})/(k_{1}-k_{2}) \\   y = (k_{1}b_{2}-k_{2}b_{1})/(k_{1}-k_{2}) \end{matrix}\right.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值