前提条件
#include <iostream>#include <cmath>#include <algorithm>using namespace std;typedef double temType; // 需要用到的类型点(向量)
已知
,点坐标或向量坐标
函数
相加、相减、与常数相加、点乘、叉乘、旋转、标准化
向量旋转
代码
// 点或向量struct point{ temType x, y; point(): x(0), y(0){} point(temType _x, temType _y): x(_x), y(_y){} point(const point &t){x = t.x;y = t.y;}; point operator-(const point& t) const{return {x-t.x, y-t.y};} // 相减 point operator+(const point& t) const{return {t.x+x, t.y+y};} // 相加 point operator*(temType t) const{return {x*t, y*t};} // 与常数相乘 temType cdot(const point& t) const{return t.x*x+t.y*y;} // 点乘 temType times(const point& t) const{return x*t.y-y*t.x;} // 叉乘 bool operator==(const point& t) const{return x==t.x && y==t.y;} bool operator!=(const point& t) const{return x!=t.x || y!=t.y;} temType length() const{return sqrt(x*x+y*y); } void normalize() { //标准化 temType sqt = sqrt(x*x+y*y); x = x/sqt; y = y/sqt; } point rotation(temType t) const{ //向量旋转 temType cost=cos(t), sint=sin(t); return {x*cost-y*sint, x*sint+y*cost}; }};typedef struct point pit;typedef struct point vtr;极坐标点
struct polar{ temType p, e; polar(temType _p, temType _e): p(_p), e(_e){}};typedef struct polar plr;
polar toPolar(const pit& t){ return {sqrt(t.x*t.x+t.y*t.y),atan2(t.y,t.x)}; }pit toRect(const plr& t){ return {t.p*cos(t.e), t.p*sin(t.e)}; }线段
已知
已知线段两端端点
函数
跨立实验,快速排斥实验
代码
// 线段struct segment{ pit A, B; segment(const pit& _A, const pit& _B): A(_A), B(_B){} pit midpoint() const{return {(A.x+B.x)/2.0, (A.y,B.y)/2.0};} // 中点 bool rapidRejectionExp(const segment& t) const { // 快速排斥实验 TODO 这里有一些错误 pit minTmp(min(A.x, B.x), min(A.y, B.y)); pit maxTmp(max(A.x, B.x), max(A.y, B.y)); return (t.A.x >= minTmp.x && t.A.x <= maxTmp.x) && (t.A.y >= minTmp.y && t.A.y <= maxTmp.y) || (t.B.x >= minTmp.x && t.B.x <= maxTmp.x) && (t.B.y >= minTmp.y && t.B.y <= maxTmp.y); } bool straddleExp(const segment& t) const{ // 跨立实验// if(!rapidRejectionExp(t)) return false; vtr AB = B-A, tba = t.A-t.B; temType t1 = AB.times(t.A-A) * AB.times(t.B-A), t2 = tba.times(A-t.B)*tba.times(B-t.B); if(t1 < 0 && t2 < 0) return true; else return false; }};typedef struct segment seg;直线与射线
已知
已知直线上一点和向量,
函数
获得直线上的一点
求两直线交点

公式
代码
struct line{ pit P; vtr s; line(const pit& _P, const vtr& _s): P(_P), s(_s) {s.normalize();} pit getPoint(temType t) const {return P + s*t;} bool ispParallel(const line& t) const{return s.times(t.s)==0;} // 平行 bool isCoincide(const line& t) const{return s.times(t.s)==0 && ((s.times(P-t.P)==0));} // 重合 pit intersection(const line& t) const{ return P - s * (t.s.times(P-t.P) / t.s.times(s)); }};typedef struct line line; // 直线typedef struct line ray; // 射线圆
已知
圆心,半径
代码
struct circle{ pit O; temType r; circle(const pit& _O, temType _r): O(_O), r(_r){}};typedef struct circle cle;直线与圆

已知
直线:点坐标,
圆:点坐标,圆的半径
推导
代码
seg intersectionOfLineAndCircle(const line& p, const cle& c){ vtr PO = c.O - p.P; temType OE = p.s.times(PO), PE = p.s.times(PO); temType tmp = sqrt(c.r*c.r-OE*OE); return {p.getPoint(PE-tmp), p.getPoint(PE+tmp)};}凸包
已知
已知一个二维坐标图中每个顶点,求最小周长能够包含所有所有顶点的凸包
思路
先将点双关键字排序,横坐标为第一关键字,纵坐标为第二关键字
排序之后

图片向量方向迭代方向
之后维护单调单调栈,其中栈顶为,栈顶第二元素为,当前检查的点为,由于凸包是不会出现右转的,所以当出现说明栈顶的元素不是最优凸包,弹出栈顶重复上一步。



第一次循环迭代之后,会发现求完了下凸包先在需要倒转迭代方向,再次遍历




第二次迭代完善上凸包,最终会形成一个最短周长的能够包含所有顶点的凸包
注意:运行结果最后还会添加一次初始节点,那么周长就为
代码
const int N = 1e5+10;pit p[N];int stk[N<<1];int tp;
bool cmp(pit &a, pit &b){ if(a.x == b.x) return a.y < b.y; return a.x < b.x;}
int main() { debug; int n; temType xt, yt; cin >> n; for(int i = 0; i < n; ++i) { cin >> xt >> yt; p[i] = point(xt, yt); } sort(p, p+n, cmp); stk[++tp] = 0; for(int i = 1; i < n; ++i) { while (tp >= 2 && ((p[stk[tp]]-p[stk[tp-1]]).times(p[i]-p[stk[tp]]) < 0)) tp--; stk[++tp] = i; } int tmp = tp; for(int i = n-1; i >= 0; --i){ while (tp > tmp && ((p[stk[tp]]-p[stk[tp-1]]).times(p[i]-p[stk[tp]]) < 0)) tp--; stk[++tp] = i; } temType ans = 0; for(int i = 1; i <= tp; ++i) { ans += (p[stk[i]] - p[stk[i-1]]).length(); } printf("%.2f", ans);}Translate by Kimi-K3
Prerequisites
#include <iostream>#include <cmath>#include <algorithm>using namespace std;typedef double temType; // the type we will usePoint (Vector)
Given
, the coordinates of a point or a vector
Operations
Addition, subtraction, addition with a constant, dot product, cross product, rotation, normalization
Vector Rotation
Code
// point or vectorstruct point{ temType x, y; point(): x(0), y(0){} point(temType _x, temType _y): x(_x), y(_y){} point(const point &t){x = t.x;y = t.y;}; point operator-(const point& t) const{return {x-t.x, y-t.y};} // subtraction point operator+(const point& t) const{return {t.x+x, t.y+y};} // addition point operator*(temType t) const{return {x*t, y*t};} // multiplication by a constant temType cdot(const point& t) const{return t.x*x+t.y*y;} // dot product temType times(const point& t) const{return x*t.y-y*t.x;} // cross product bool operator==(const point& t) const{return x==t.x && y==t.y;} bool operator!=(const point& t) const{return x!=t.x || y!=t.y;} temType length() const{return sqrt(x*x+y*y); } void normalize() { //normalization temType sqt = sqrt(x*x+y*y); x = x/sqt; y = y/sqt; } point rotation(temType t) const{ //vector rotation temType cost=cos(t), sint=sin(t); return {x*cost-y*sint, x*sint+y*cost}; }};typedef struct point pit;typedef struct point vtr;Point in Polar Coordinates
struct polar{ temType p, e; polar(temType _p, temType _e): p(_p), e(_e){}};typedef struct polar plr;
polar toPolar(const pit& t){ return {sqrt(t.x*t.x+t.y*t.y),atan2(t.y,t.x)}; }pit toRect(const plr& t){ return {t.p*cos(t.e), t.p*sin(t.e)}; }Segment
Given
The two endpoints of the segment are known
Operations
Straddle test, rapid rejection test
Code
// segmentstruct segment{ pit A, B; segment(const pit& _A, const pit& _B): A(_A), B(_B){} pit midpoint() const{return {(A.x+B.x)/2.0, (A.y,B.y)/2.0};} // midpoint bool rapidRejectionExp(const segment& t) const { // rapid rejection test TODO there are some errors here pit minTmp(min(A.x, B.x), min(A.y, B.y)); pit maxTmp(max(A.x, B.x), max(A.y, B.y)); return (t.A.x >= minTmp.x && t.A.x <= maxTmp.x) && (t.A.y >= minTmp.y && t.A.y <= maxTmp.y) || (t.B.x >= minTmp.x && t.B.x <= maxTmp.x) && (t.B.y >= minTmp.y && t.B.y <= maxTmp.y); } bool straddleExp(const segment& t) const{ // straddle test// if(!rapidRejectionExp(t)) return false; vtr AB = B-A, tba = t.A-t.B; temType t1 = AB.times(t.A-A) * AB.times(t.B-A), t2 = tba.times(A-t.B)*tba.times(B-t.B); if(t1 < 0 && t2 < 0) return true; else return false; }};typedef struct segment seg;Line and Ray
Given
A point on the line and a vector are known,
Operations
Get a point on the line
Finding the Intersection of Two Lines

Formula
Code
struct line{ pit P; vtr s; line(const pit& _P, const vtr& _s): P(_P), s(_s) {s.normalize();} pit getPoint(temType t) const {return P + s*t;} bool ispParallel(const line& t) const{return s.times(t.s)==0;} // parallel bool isCoincide(const line& t) const{return s.times(t.s)==0 && ((s.times(P-t.P)==0));} // coincident pit intersection(const line& t) const{ return P - s * (t.s.times(P-t.P) / t.s.times(s)); }};typedef struct line line; // linetypedef struct line ray; // rayCircle
Given
Center , radius
Code
struct circle{ pit O; temType r; circle(const pit& _O, temType _r): O(_O), r(_r){}};typedef struct circle cle;Line and Circle

Given
Line: coordinates of point ,
Circle: coordinates of point , radius of the circle
Derivation
Code
seg intersectionOfLineAndCircle(const line& p, const cle& c){ vtr PO = c.O - p.P; temType OE = p.s.times(PO), PE = p.s.times(PO); temType tmp = sqrt(c.r*c.r-OE*OE); return {p.getPoint(PE-tmp), p.getPoint(PE+tmp)};}Convex Hull
Given
Given every vertex in a 2D coordinate plane, find the convex hull with the minimum perimeter that contains all the vertices
Approach
First sort the points by two keys, with the x-coordinate as the primary key and the y-coordinate as the secondary key
After sorting

The vector direction in the picture is the iteration direction
Then maintain a monotonic stack, where the top of the stack is , the second element from the top is , and the point currently being checked is . Since a convex hull never makes a right turn, when it means the element at the top of the stack is not part of the optimal convex hull, so pop the top and repeat the previous step.



After the first iteration, you will find that the lower hull has been computed; now you need to reverse the iteration direction and traverse again




The second iteration completes the upper hull, eventually forming a convex hull with the shortest perimeter that contains all vertices
Note: the result will add the initial node once more at the end, so the perimeter is
Code
const int N = 1e5+10;pit p[N];int stk[N<<1];int tp;
bool cmp(pit &a, pit &b){ if(a.x == b.x) return a.y < b.y; return a.x < b.x;}
int main() { debug; int n; temType xt, yt; cin >> n; for(int i = 0; i < n; ++i) { cin >> xt >> yt; p[i] = point(xt, yt); } sort(p, p+n, cmp); stk[++tp] = 0; for(int i = 1; i < n; ++i) { while (tp >= 2 && ((p[stk[tp]]-p[stk[tp-1]]).times(p[i]-p[stk[tp]]) < 0)) tp--; stk[++tp] = i; } int tmp = tp; for(int i = n-1; i >= 0; --i){ while (tp > tmp && ((p[stk[tp]]-p[stk[tp-1]]).times(p[i]-p[stk[tp]]) < 0)) tp--; stk[++tp] = i; } temType ans = 0; for(int i = 1; i <= tp; ++i) { ans += (p[stk[i]] - p[stk[i-1]]).length(); } printf("%.2f", ans);}