计算几何从入门到入土(题目)

代码中的板子参考了繁凡和俊杰带佬的板子

二维基础

A.计算几何spj hacker

题意:

输入一组数a,b使无论ans为任何数,以下函数都返回AC。

// a and b are outputed by the contestant
// ans is provided by the jury
int check(int a,int b,double ans)
{
	double t=(double)a/(double)b;
	if(fabs(t-ans)>1e-9)
        return WA;
	return AC;
}

题解:

13fd265df90ffc12c.png
当a/b为nan值时,返回True。

代码:

//php
0 0

B.Winding Number

题意:

牛妹沿着 n n n个点走,询问牛牛在某点观察到牛妹逆时针走的圈数与顺时针的圈数之差。

题解:

回转数板子

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
typedef pair<bool, int>Pr;
//constexpr double eps = 1e-8;
constexpr long long eps=0;
constexpr double PI = 3.1415926535897932384l;

template<typename T> struct point
{
	T x, y;

	bool operator==(const point& a) const { return (abs(x - a.x) <= eps && abs(y - a.y) <= eps); }
	bool operator<(const point& a) const { if (abs(x - a.x) <= eps) return y < a.y - eps; return x < a.x - eps; }
	point operator+(const point& a) const { return { x + a.x,y + a.y }; }
	point operator-(const point& a) const { return { x - a.x,y - a.y }; }
	point operator-() const { return { -x,-y }; }
	point operator*(const T k) const { return { k * x,k * y }; }
	point operator/(const T k) const { return { x / k,y / k }; }
	T operator*(const point& a) const { return x * a.x + y * a.y; } //点乘 Dot
	T operator^(const point& a) const { return x * a.y - y * a.x; } //叉乘 Cross
	// 1:左  -1:右  0:线上
	//auto的原理就是根据后面的值,来自己推测前面的类型是什么。
	//return *this返回当前对象, return this返回当前对象的地址(指向当前对象的指针)
	int toleft(const point& a) const { const auto t = (*this) ^ a; return (t > eps) - (t < -eps); }
	T len2() const { return (*this) * (*this); }
	T dis2(const point& a) const { return (a - (*this)).len2(); }
	double len() const { return sqrt(len2()); }
	double dis(const point& a) const { return sqrt(dis2(a)); }
	//夹角
	double ang(const point& a) const { return acos(max(-1.0, min(1.0, ((*this) * a) / (len() * a.len())))); }
	//逆时针旋转rad
	point rot(const double rad) const { return { x * cos(rad) - y * sin(rad),x * sin(rad) + y * cos(rad) }; }
};

using Point = point<ll>;

template<typename T> struct line
{
	point<T> p, v;

	bool operator==(const line& a) const { return v.toleft(a.v) == 0 && v.toleft(p - a.p) == 0; }
	int toleft(const point<T>& a) const { return v.toleft(a - p); }
	//直线交点
	point<T> inter(const line& a) const { return p + v * ((a.v ^ (p - a.p)) / (v ^ a.v)); }
	//点到直线的距离
	double dis(const point<T>& a) const { return abs(v ^ (a - p)) / v.len(); }
	//a在直线上的投影点
	point<T> proj(const point<T>& a) const { return p + v * ((v * (a - p)) / (v * v)); }
};

using Line = line<ll>;

template<typename T> struct polygon
{
	vector<Point> p;
	//返回下一个点和前一个点
	//size_t可理解为unsigned int,应为数组下标类型
	inline size_t nxt(const size_t i) const { return i == p.size() - 1 ? 0 : i + 1; }
	inline size_t pre(const size_t i) const { return i == 0 ? p.size() - 1 : i - 1; }

	pair<bool, int> winding(const Point& a) const
	{
		int cnt = 0;
		for (size_t i = 0; i < p.size(); i++)
		{
			Point u = p[i], v = p[nxt(i)];
			if (abs((a - u) ^ (a - v)) <= eps && (a - u) * (a - v) <= eps) return { true,0 };//点在边上
			if (abs(u.y - v.y) <= eps) continue;
			Line uv = { u,v - u };
			if (u.y < v.y - eps && uv.toleft(a) <= 0) continue;//该射线与边无交点
			if (u.y > v.y + eps && uv.toleft(a) >= 0) continue;
			//计算wn数
			if (u.y < a.y - eps && v.y >= a.y - eps) cnt++;
			if (u.y >= a.y - eps && v.y < a.y - eps) cnt--;
		}
		return { false,cnt };
	}
	//边长
	double circ() const
	{
		double sum = 0;
		for (size_t i = 0; i < p.size(); i++) sum += p[i].dis(p[nxt(i)]);
		return sum;
	}

	T area2() const
	{
		T sum = 0;
		for (size_t i = 0; i < p.size(); i++) sum += p[i] ^ p[nxt(i)];
		return abs(sum);
	}
};

using Polygon = polygon<ll>;


int n, m;

int main() {
	cin >> n;
	Polygon P;
	for (int i = 0; i < n; i++) {
		int x, y; cin >> x >> y;
		P.p.push_back({ x,y });
	}
	cin >> m;
	while (m--) {
		int sx, sy; cin >> sx >> sy;
		Pr S = P.winding({ sx,sy });
		if (S.first)cout << "EDGE\n";
		else cout << S.second << "\n";
	}
	return 0;
}

C.Mr. Main and Windmills

代码:

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<bits/stdc++.h>
#define db double
using namespace std;
const int maxn = 1e3 + 10;
const int inf = 1e9;
double nx[maxn], ny[maxn];
int sx, sy, tx, ty;
int N, M;

struct Point {
    double x, y;
    Point(double x = 0, double y = 0) :x(x), y(y) {}
};

vector<Point>vec[maxn];
vector<Point>vec1[maxn];

typedef Point Vector;

Vector operator + (Vector A, Vector B) {
    return Vector(A.x + B.x, A.y + B.y);
}

Vector operator - (Point A, Point B) {
    return Vector(A.x - B.x, A.y - B.y);
}

Vector operator * (Vector A, double p) {
    return Vector(A.x * p, A.y * p);
}

Vector operator / (Vector A, double p) {
    return Vector(A.x / p, A.y / p);
}

struct Line {//直线定义
    Point v, p;
    Line(Point v, Point p) :v(v), p(p) {}
    Point point(double t) {//返回点P = v + (p - v)*t
        return v + (p - v) * t;
    }
};

double Cross(Vector A, Vector B) {
    return A.x * B.y - A.y * B.x;
}

Point GetLineIntersection(Point P, Vector v, Point Q, Vector w) {
    Vector u = P - Q;
    double t = Cross(w, u) / Cross(v, w);
    return P + v * t;
}

double Dot(Vector A, Vector B) {
    return A.x * B.x + A.y * B.y;
}

const double eps = 1e-15;

int dcmp(double x, double y) {
    if (fabs(x - y) < eps)
        return 0;
    if (x > y)
        return 1;
    return -1;
}

int sgn(double x) {
    if (fabs(x) < eps)
        return 0;
    if (x < 0)
        return -1;
    return 1;
}

bool OnSegment(Point p, Point a1, Point a2) {
    return  sgn(Dot(a1 - p, a2 - p)) < 0 ;//&& sgn(Cross(a1 - p, a2 - p)) == 0;
}

bool up(Point a, Point b) {
    return a.x > b.x;
}

bool low(Point a, Point b) {
    return a.x < b.x;
}


int main() {
    cin >> N >> M;
    cin >> sx >> sy >> tx >> ty;
    Point S(sx, sy);
    Point T(tx, ty);
    Point P[maxn];
    Line car(S, T);
    for (int i = 1; i <= N; i++) {
        cin >> P[i].x >> P[i].y;
    }
    int f = 0;
    if (sx > tx)f = 1;

    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= N; j++) {
            Line feng(P[i], P[j]);
            if (i == j)continue;
            if (Cross(S - T, P[i] - P[j]) == 0)continue;
            Point d = GetLineIntersection(S, T - S, P[i], P[j] - P[i]);
            if (OnSegment(d, S, T))vec[i].push_back(d);
        }
        int len = vec[i].size();
        if (f)sort(vec[i].begin(), vec[i].end(), up);
        else sort(vec[i].begin(), vec[i].end(), low);

    }
    while (M--) {
        int p, k; cin >> p >> k;
        int tt=vec[p].size();
        if (tt < k)cout << -1 << endl;
        else printf("%.10f %.10f\n", vec[p][k - 1].x, vec[p][k - 1].y);
    }
    return 0;
}

E.Operation Love

题意:

在二维坐标图给出一个手掌状图形,判断该图形是左手还是右手。

题解:

7KqYq0.png

枚举所有线段,通过线段长度找到A,B,C三个点,若 B C ⃗ \vec{BC} BC A B ⃗ \vec{AB} AB 的逆时针方向为左手,否则为右手。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const double eps = 1e-6;

struct Point {
    double x, y;
    Point(double x = 0, double y = 0) :x(x), y(y) {}
};

int sgn(double x) {
    if (fabs(x) < eps)return  0;
    if (x < 0)return -1;
    return 1;
}


typedef Point Vector;

Vector operator + (Vector A, Vector B) { return Vector(A.x + B.x, A.y + B.y); }
Vector operator - (Point A, Point B) { return Vector(A.x - B.x, A.y - B.y); }
Vector operator * (Vector A, double p) { return Vector(A.x * p, A.y * p); }
Vector operator / (Vector A, double p) { return Vector(A.x / p, A.y / p); }
bool operator < (const Point& a, const Point& b) { return a.x < b.x || (a.x == b.x && a.y < b.y); }
bool operator ==(const Point& a, const Point& b) { return !sgn(a.x - b.x) && !sgn(a.y - b.y); }

//点积,可用于判断角度
double Dot(Vector A, Vector B) { return A.x * B.x + A.y * B.y; }

//叉积
double Cross(Vector A, Vector B) { return A.x * B.y - B.x * A.y; }

//模长
double Length(Vector A) { return sqrt(Dot(A, A)); }

//判断 bc 是不是在 ab 的逆时针方向,向量夹角<90
bool ToLeftTest(Point a, Point b, Point c) { return Cross(b - a, c - b) > 0; }


int T;

int main() {
    ios::sync_with_stdio(0);
    cin >> T;
    while (T--) {
        Point p[30], P1, P2, P3, P4;
        for (int i = 1; i <= 20; i++)cin >> p[i].x >> p[i].y;
        p[21] = p[1], p[22] = p[2];
        p[0] = p[20];
        for (int i = 1; i <= 20; i++) {
            double L = Length(p[i + 1] - p[i]);
            if (L - 8.5 > eps) {
               // P1 = p[i], P2 = p[i + 1];
                double L1 = Length(p[i - 1] - p[i]), L2 = Length(p[i + 2] - p[i + 1]);
                if (L2 - L1 > eps)P1 = p[i], P2 = p[i + 1], P3 = p[i - 1];
                if (L1 - L2 > eps)P1 = p[i + 1], P2 = p[i], P3 = p[i + 2];
                break;
            }
        }
        if (!ToLeftTest(P2, P1, P3))cout << "right\n";
        else cout << "left\n";
    }
	return 0;
}

F.三角碰撞

题意

在一个三角形内,小球具有一个起始位置,方向及速度,遇到三角形的边会发生反弹,询问反弹 K 次的时间。

题解

S08V9E___GXF0APBQL.png

大部分的反射问题都可以通过,将扩大延长原有图像的方法解决。

如图,我们可以把问题转化成一条具有方向的射线,需要多长时间,刚好与图中的三种直线有 k 个交点。

我们先看第一种直线。

UC3X9GCL73.png

我们只需要二分时间,得到该射线的终点,计算该条线段在y轴上的投影是三角形的高的几倍(红线与X轴之间蓝线的数量),倍数即是交点数。

对于第二,三种直线,我们需要通过坐标系变换,得到新的坐标,可通过第一种直线的方法解决。

坐标系变换,即是将X,Y轴逆时针旋转120°和240°,变换公式如下

[ cos ⁡ θ − sin ⁡ θ sin ⁡ θ cos ⁡ θ ] [ a x a y ] = [ cos ⁡ θ a x − sin ⁡ θ a y sin ⁡ θ a x + c o s θ a y ] \begin{bmatrix} \cos\theta&-\sin\theta\\ \sin\theta&\cos\theta\\ \end{bmatrix} \begin{bmatrix} a_x\\ a_y\\ \end{bmatrix}= \begin{bmatrix} \cos\theta a_x-\sin\theta a_y\\ \sin\theta a_x+cos\theta a_y\\ \end{bmatrix} [cosθsinθsinθcosθ][axay]=[cosθaxsinθaysinθax+cosθay]

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const double eps = 1e-5;
const double gen3 = 1.7320508075688772935;
int T;
double L, x, y, vx, vy, k;


bool ok(double len) {
	double res = 0, d = gen3 * L / 2.0;
	res += abs(floor((y + (len * vy)) / d));
	double py = fabs((-y - gen3 * x + d) / 2), pvy = (-vy - gen3 * vx) / 2;
	res+= abs(floor((py + (len * pvy)) / d));
	py = fabs((-y + gen3 * x + d) / 2), pvy = (-vy + gen3 * vx) / 2;
	res += abs(floor((py + (len * pvy)) / d));
	return res >= k;
}

int main() {
	cin >> T;
	while (T--) {
		cin >> L >> x >> y >> vx >> vy >> k;
		double lb = 0, rb = 1e11;
		double mid = 0;
		while (rb - lb > eps) {
			mid = (rb + lb) / 2;
			if (ok(mid))rb = mid;
			else lb = mid;
		}
		printf("%.10f\n",mid);
	}
	return 0;
}

G.Magic Rabbit

题意

有三种体积相同,含有X溶液$ a_i ml/l$,Y溶液 b i m l / l b_i ml/l biml/l的混合溶液,询问能否得到题目给出的不同浓度的混合溶液。

题解

我们在坐标系中将点描出并连起来,会发现这个其实是一个线性规划问题,坐标系中围成的区域就是能够配出的溶液配比。

因此,这道题可以转化成求某点是否在三角形内。

特别的是,这个三角形可能是退化的三角形。

我们需要通过 to_left 测试判断该点是否都在所有边的左边或者右边,或者在某一条边上即可。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const double eps = 1e-4;
struct Point { double x, y; };
vector<Point>P;

int to_left(Point a, Point b, Point c) {
	Point X = { b.x - a.x,b.y - a.y }, Y = { c.x - a.x,c.y - a.y };
	double ans = X.x * Y.y - X.y * Y.x ;
	double res = X.x * Y.x + Y.y * X.y;
	if (abs(ans) < eps) {
		return 0;
	}
	else if (ans > 0)return 1;
	return -1;
}

int main() {
	for (int i = 0; i < 3; i++) {
		Point p; cin >> p.x >> p.y;
		P.push_back(p);
	}
	P.push_back(P[0]);
	int T; cin >> T;
	while (T--) {
		Point p; cin >> p.x >> p.y;
		int f1 = 0, f2 = 0, f = 0;
		for (int i = 0; i < 3; i++) {
			int cnt = to_left(P[i], P[i + 1], p);
			if (cnt == 1)f1++;
			else if (cnt == -1)f2++;
			else {
				if (p.x >= min(P[i].x, P[i + 1].x) && p.x <= max(P[i].x, P[i + 1].x) && p.y >= min(P[i].y, P[i + 1].y) && p.y <= max(P[i].y, P[i + 1].y)) {
					f = 1;
				}
			}
		}
		if (f1 == 3 || f2 == 3 || f == 1)cout << "YES\n";
		else cout << "NO\n";
	}
	return 0;
}

极角排序

A.Sort Points by Argument

题意

对每个点的极角进行排序

板子题

题解

简单解释一下板子

9JB8VEMVK5_SWE5TSMY.png

我们将平面分为五个部分,分别为 x 轴以下,原点,x 轴正半轴,x 轴以上,x 轴以上。

对于某一部分通过叉积来判断大小。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
//constexpr double eps = 1e-8;
constexpr long long eps = 0;
constexpr double PI = 3.1415926535897932384l;

template<typename T> struct point
{
	T x, y;

	bool operator==(const point& a) const { return (abs(x - a.x) <= eps && abs(y - a.y) <= eps); }
	bool operator<(const point& a) const { if (abs(x - a.x) <= eps) return y < a.y - eps; return x < a.x - eps; }
	point operator+(const point& a) const { return { x + a.x,y + a.y }; }
	point operator-(const point& a) const { return { x - a.x,y - a.y }; }
	point operator-() const { return { -x,-y }; }
	point operator*(const T k) const { return { k * x,k * y }; }
	point operator/(const T k) const { return { x / k,y / k }; }
	T operator*(const point& a) const { return x * a.x + y * a.y; } //点乘 Dot
	T operator^(const point& a) const { return x * a.y - y * a.x; } //叉乘 Cross
	// 1:左  -1:右  0:线上
	//auto的原理就是根据后面的值,来自己推测前面的类型是什么。
	//return *this返回当前对象, return this返回当前对象的地址(指向当前对象的指针)
	int toleft(const point& a) const { const auto t = (*this) ^ a; return (t > eps) - (t < -eps); }
	T len2() const { return (*this) * (*this); }
	T dis2(const point& a) const { return (a - (*this)).len2(); }
	double len() const { return sqrt(len2()); }
	double dis(const point& a) const { return sqrt(dis2(a)); }
	//夹角
	double ang(const point& a) const { return acos(max(-1.0, min(1.0, ((*this) * a) / (len() * a.len())))); }
	//逆时针旋转rad
	point rot(const double rad) const { return { x * cos(rad) - y * sin(rad),x * sin(rad) + y * cos(rad) }; }
};

using Point = point<ll>;

bool argcmp(const Point& a, const Point& b) {
	auto quad = [](const Point& a) {
		if (a.y < -eps)return 1;
		if (a.y > eps)return 4;
		if (a.x < -eps)return 5;
		if (a.x > eps)return 3;
		return 2;
	};
	int qa = quad(a), qb = quad(b);
	if (qa != qb)return qa < qb;
	auto t = a ^ b;
	//极轴排序
	//if (abs(t) <= eps)return a * a < b* b - eps;
	return t > eps;
}

ll n, x, y;
vector<Point>vec;

int main() {
	cin >> n;
	for (int i = 0; i < n; i++) {
		Point p; cin >> p.x >> p.y;
		vec.push_back(p);
	}
	sort(vec.begin(), vec.end(), argcmp);
	
	for (int i = 0; i < vec.size(); i++) {
		cout << vec[i].x << " " << vec[i].y << "\n";
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值