是时候建立自己的计算几何模板了
计算几何精度问题
定义点
struct point
{
double x, y;
point() {}
point(double x, double y) :x(x), y(y) {}
point operator +(point B) { return point(B.x + x, B.y + y); }
point operator -(point B) { return point(x - B.x, y - B.y); }
point operator /(double B) { return point(x / B, y / B); }
point operator *(double B) { return point(B * x, B * y); }
//绕原点旋转角度B(弧度值),后x,y的变化
void transXY(double B)
{
double tx = x,ty = y;
x = tx*cos(B) - ty*sin(B);
y = tx*sin(B) + ty*cos(B);
}
}
定义线段
struct line
{
point s,e;
line(point _s,point _e)
{
s = _s;e = _e;
}
};
判断线段相交(非规范)
bool inter(line l1,line l2)
{
return
max(l1.s.x,l1.e.x) >= min(l2.s.x,l2.e.x) &&
max(l2.s.x,l2.e.x) >= min(l1.s.x,l1.e.x) &&
max(l1.s.y,l1.e.y) >= min(l2.s.y,l2.e.y) &&
max(l2.s.y,l2.e.y) >= min(l1.s.y,l1.e.y) &&
sgn((l2.s-l1.s)^(l1.e-l1.s))*sgn((l2.e-l1.s)^(l1.e-l1.s)) <= 0 &&
sgn((l1.s-l2.s)^(l2.e-l2.s))*sgn((l1.e-l2.s)^(l2.e-l2.s)) <= 0;
}
规范相交
《算法竞赛入门到进阶》P280
定义有向线段
struct line
{
point p;
vec v;
double ang;
line(point p1, vec v1) :p(p1), v(v1) { ang = atan2(v.y, v.x); }
bool operator <(line L) { return ang < L.ang; }//极角排序
};
//atan2(y,x),x可以为0
点乘
double Dot(vec A, vec B) { return A.x*B.x + A.y*B.y; }
矢量长度
double lec(vec A) { return sqrt(Dot(A, A)); }
double lec2(vec A) { return (Dot(A, A)); }
叉乘
double cross(vec A, vec B) { return A.x*B.y - A.y*B.x; }
点线距、点在线上投影、点关于线的对称点等,见《算法竞赛入门到进阶》P277-280
两条线整数交点
参考博客
POJ 3304
寻找和所有线段相交的直线
线段与多边形的关系
见《算法竞赛入门到进阶》P294-295
判断圆与多边形的关系:参考博客
(同时也可以判断点与多边形的关系)
参考博客
(分治法)
参考博客
旋转卡壳详解参考博客
求最远点对(凸包+旋转卡壳)
#include
#include
#include
#include
using namespace std;
const int MAXN = 50010;
typedef long long ll;
const double eps = 1e-8;
struct Point {
double x, y;
};
Point list[MAXN];
int stack[MAXN], top;
/***
* 叉积
* a×b>0, 则b在a的逆时针方向;
* a×b<0, 则b在a的顺时针方向;
* a×b=0, 则a与b共线,但可能同向也可能反向。
*/
double crossProduct(Point a, Point b) {
return a.x*b.y - a.y*b.x;
}
int sgn(double x) {
if (fabs(x) < eps) return 0;
if (x < 0) return -1;
else return 1;
}
Point sub(Point a, Point b) {
Point p;
p.x = a.x - b.x;
p.y = a.y - b.y;
return p;
}
double dist(Point a, Point b) {
return (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y);
}
//相对于极点list[0]的极角排序
bool cmp(Point p1, Point p2) {
double temp = crossProduct(sub(p1, list[0]), sub(p2, list[0]));
if (sgn(temp) > 0) return true;
else if (sgn(temp) == 0 && sgn(dist(p1, list[0]) - dist(p2, list[0])) <= 0) return true;
else return false;
}
/*
* 求凸包,Graham算法
* 点的编号0~n-1
* 返回凸包结果Stack[0~top-1]为凸包的编号
*/
void Graham(int n)
{
Point p0 = list[0];
int k = 0;
for (int i = 1; i < n; i++)
{
if (p0.y > list[i].y || (p0.y == list[i].y && p0.x > list[i].x))
{
p0 = list[i];
k = i;
}
}
swap(list[k], list[0]);
sort(list + 1, list + n, cmp);
stack[0] = 0;
if (n == 1) { top = 1; return; }
stack[1] = 1;
if (n == 2) { top = 2; return; }
top = 2;
for (int i = 2; i < n; i++)
{
while (top > 1 && sgn(crossProduct(sub(list[stack[top - 1]], list[stack[top - 2]]), sub(list[i], list[stack[top - 2]]))) <= 0)
{
top--;
}
stack[top++] = i;
}
}
double RC(int top)//旋转卡壳
{
double ans = 0;
int now = 2;
for (int i = 0; i < top; i++)
{
while (crossProduct(sub(list[stack[i + 1]], list[stack[i]]), sub(list[stack[now]], list[stack[i]])) < crossProduct(sub(list[stack[i + 1]], list[stack[i]]), sub(list[stack[(now + 1) % top]], list[stack[i]])))
{
now++; //最远距离对应了最大面积。
if (now == top) now = 0;
}
ans = max(ans, dist(list[stack[now]], list[stack[i]]));
}
return ans;
}
int main()
{
int N;
scanf_s("%d", &N);
for (int i = 0; i < N; i++)
scanf_s("%lf%lf", &list[i].x, &list[i].y);
Graham(N);
printf("%lld", (ll)RC(top));
return 0;
}
某巨佬的旋转卡壳专栏
旋转卡壳动图
求空间散点组成的最大三角形面积
对n个点,分别固定他们,然后用旋转卡壳找另两个点(因为三角形的一个点对于对应的边一定是对踵点)
参考博客
POJ 1556
POJ 2966
HPI() 模板参考《算法竞赛入门到进阶》 P292
HDU 4316
凸包+半平面交
get知识:求多面体在某一平面的投影,只需把顶点的投影计算出来,然后求凸包
偶然看到的向量模板参考博客
原题地址
代码:
自闭了一下午,调出来了(现场赛这个手速,要被队友拉去煲汤)
#include
#include
#include
#include
using namespace std;
const int MAXN = 110;
typedef long long ll;
const double eps = 1e-8;
int sgn(double x)
{
if (fabs(x) < eps) return 0;
return x < 0 ? -1 : 1;
}
struct point
{
double x, y;
point() {}
point(double x, double y) :x(x), y(y) {}
point operator +(point B) { return point(B.x + x, B.y + y); }
point operator -(point B) { return point(x - B.x, y - B.y); }
point operator /(double B) { return point(x / B, y / B); }
point operator *(double B) { return point(B * x, B * y); }
}pb, pa;
typedef point vec;
double Dot(vec A, vec B) { return A.x*B.x + A.y*B.y; }
double lec(vec A) { return sqrt(Dot(A, A)); }
double lec2(vec A) { return (Dot(A, A)); }
double cross(vec A, vec B) { return A.x*B.y - A.y*B.x; }
double dist(point A, point B) { return sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y)); }
struct line
{
point p;
vec v;
double ang;
//line(point p1, point p2) :p(p1), v(p2 - p1) { ang = atan2(p2.y - p1.y, p2.x - p1.x); }
line(point p1, vec v1) :p(p1), v(v1) { ang = atan2(v.y, v.x); }
bool operator <(line L) { return ang < L.ang; }
};
struct cir
{
double x, y;
double r;
point o;
cir(double x, double y, double r) :x(x), y(y), r(r) { o = point(x, y); }
};
point p_l_proj(point p, line l)//投影
{
double k = Dot(l.v, p - l.p) / lec2(l.v);
return l.p + l.v*k;
}
bool p_o_l(point p, line l)//点在线上
{
//vec ww(p.x - l.p.x, p.y - l.p.y);
if (sgn(cross(p - l.p, l.v))==0 && Dot(p - l.p, l.v) > 0) return true;
return false;
}
double d_p_l(point p, line l)//点线距
{
point p1 = l.p + l.v;
return fabs(cross(p - l.p, l.v) / lec(l.v));
}
int l_c_r(line l, cir cr)//圆与线关系
{
double dst = d_p_l(cr.o, l);
if (Dot(cr.o - l.p, l.v) <= 0) return 0;//钝角逃逸
if (sgn(dst - cr.r) >= 0) return 0;
//else if (sgn(dst - cr.r) == 0) return 1;
return 2;
}
int l_c_c(line l, cir cr)//圆线交点
{
if (!l_c_r(l, cr)) return 0;
point p = p_l_proj(cr.o, l);
double d = d_p_l(cr.o, l);
double k = sqrt(cr.r*cr.r - d * d);
point n = (l.v) / lec(l.v);
pb = p - n * k;
pa = p + n * k;
return 1;
}
point p_l_sym(point p, line l)//对称点
{
point q = p_l_proj(p, l);
return point(2 * q.x - p.x, 2 * q.y - p.y);
}
int T;
int main()
{
ios::sync_with_stdio(false);
int cas = 0, i;
double ax, bx, r, ox, oy, ay, by, vx, vy;
cin >> T;
while (T--)
{
++cas;
cin >> ox >> oy >> r;
cir cr(ox, oy, r);
cin >> ax >> ay >> vx >> vy;
cin >> bx >> by;
point A(ax, ay), B(bx, by);
point va(vx, vy);
line l(A, va);
if (p_o_l(B, l))
{
if (l_c_r(l, cr)) //cout << "Case #" << cas << ": " << "No" << endl;
{
/*
智障了,之前这里没考虑
0 0 1
0 11 0 -1
0 4
这样的数据,自闭了一下午
*/
l_c_c(l, cr);
point pans;
if (dist(A, pa) > dist(A, pb)) pans = pb;
if (dist(pans, A) >= dist(A,B))
{
cout << "Case #" << cas << ": " << "Yes" << endl;
}
else cout << "Case #" << cas << ": " << "No" << endl;
}
else cout << "Case #" << cas << ": " << "Yes" << endl;
continue;
}
int w = l_c_c(l, cr);
if (!w)
{
cout << "Case #" << cas << ": " << "No" << endl;
continue;
}
point pans;
if (dist(A, pa) > dist(A, pb)) pans = pb;
else pans = pa;
vec vk(pans.x - cr.x, pans.y - cr.y);
vec mk(vk.y, -vk.x);//圆的切线
if ((Dot(l.v, mk) < 0)) mk = point(-mk.x, -mk.y);//貌似这行是多余的
line mir(pans, mk);
point a2 = p_l_sym(A, mir);
line l2 = line(a2, pans - a2);
if (p_o_l(B, l2) && Dot(B - pans, pans - a2) >= 0)
{
cout << "Case #" << cas << ": " << "Yes" << endl;
}
else
{
cout << "Case #" << cas << ": " << "No" << endl;
}
}
return 0;
}
貌似有大佬说要开long double,否则精度不够,不知道是不是数据被削弱了参考博客
目前只整理了二维计算几何,三维计算几何这种不属于阳间的东西 以后再补