附上一水题
double eps = 1e-8;
struct Point {
double x, y;
Point () {}
Point (double _x, double _y) : x(_x), y(_y) {}
void Read() {
scanf("%lf%lf", &x, &y);
}
Point operator + (const Point &a) const { return Point(x + a.x, y + a.y); }
// 叉积=0是指两向量平行(重合)
Point operator - (const Point &a) const { return Point(x - a.x, y - a.y); }
double operator ^ (const Point &a) const { return x * a.y - y * a.x; } // 叉积
double operator * (const Point &a) const { return x * a.x + y * a.y; } // 点积
// 定义给map set 之类用的.. 不要用极角 可能会出错
bool operator < (const Point &a) const {
if (x != a.x) return x < a.x;
return y < a.y;
}
};
struct Triangle {
Point pt1, pt2, pt3;
};
typedef Point Vector;
double GetLenght(Vector A) { return A * A; } // 获取长度
double GetLenght(Point A, Point B) { return sqrt((A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y)); }
double GetAngle(Vector A, Vector B) { return acos( (A*B) / GetLenght(A) / GetLenght(B)); } // 获取夹角[-pi/2, pi/2]
double GetArea(Vector A, Vector B) { return A ^ B; } // 四边形面积, 有正负
// AB --> AC A到B: B-A
double GetArea(Point A, Point B, Point C) { return GetArea(B-A, C-A); } // 四边形面积, 有正负
double GetCross(Point st, Point ed1, Point ed2) { return (ed1 - st) ^ (ed2 - st); }
/*点定位*/
// 点是否在线段上
// 原理:
// 充要条件: 三点共线 同时 jpt 点在 线段 之间
// --------> 三点共线 同时 jpt 在st ed组成的矩阵里
bool IsPointInSegment(const Point &jpt, const Point &st, const Point &ed)
{
if (GetCross(jpt, st, ed) == 0.0 &&
min(st.x, ed.x) <= jpt.x &&
max(st.x, ed.x) >= jpt.x &&
max(st.y, ed.y) <= jpt.y &&
max(st.y, ed.y) >= jpt.y)
return true;
return false;
}
// 点是否在三角形内部
// 原理:
// 点与三角形三个顶点构成的三个三角形的面积之和 与 三角形是否一样.
// 如果一样说明点在三角形内部
bool IsPointInTriangle(const Point &jpt, const Triangle &t)
{
double s,s1, s2, s3;
s = fabs(GetArea(t.pt1 - t.pt2, t.pt3 - t.pt2));
s1 = fabs(GetArea(t.pt1 - jpt, t.pt2 - jpt));
s1 = fabs(GetArea(t.pt1 - jpt, t.pt3 - jpt));
s1 = fabs(GetArea(t.pt2 - jpt, t.pt3 - jpt));
return s == s1 + s2 + s3;
}
// 点在多边形内外
// 一 扫描法
// 二 叉乘判别法
// 三 角度和判别法
// 四 特别的 点在矩形或园中.
// 点是否在矩形
// 矩形点是 st 和 ed
bool IsPointInRectangle(const Point &jpt, const Point &st, const Point &ed)
{
if (min(st.x, ed.x) <= jpt.x &&
max(st.x, ed.x) >= jpt.x &&
min(st.y, ed.y) <= jpt.y &&
max(st.y, ed.y) >= jpt.y)
return true;
return false;
}
/*
两线段相交分为"规范相交"和"非规范相交"。
"规范相交"指的是两条线段恰有唯一一个不是端点的公共点;
而如果一条线段的一个端点在另一条线段上,或者两条线段部分重合,则视为“非规范相交”,
*/
bool IsSegmentIntersect(const Point &A, const Point &B, const Point &C, const Point &D) {
// 快速排斥 好理解版本 判断在不在矩形内. 重叠情况也可以判断
if (!IsPointInRectangle(C, A, B) && !IsPointInRectangle(D, A, B)) return false;
if (GetCross(A, C, B) * GetCross(A, D, B) <= 0.0 &&
GetCross(C, A, D) * GetCross(C, B, D) <= 0.0)
return true;
return false;
// 前3句是快速排斥
// 后2句是跨立实验 <= 表示允许重叠 端点在另一线段上 < 则不允许
/* if (max(A.x, B.x) >= min(C.x, D.x) &&
min(A.x, B.x) <= max(C.x, D.x) &&
max(A.y, B.y) >= min(C.y, D.y) &&
GetCross(A, C, B) * GetCross(A, D, B) <= 0.0 &&
GetCross(C, A, D) * GetCross(C, B, D) <= 0.0 )
return true;
return false;*/
}
struct Segment {
Point st;
Point ed;
int id;
void Read() {
scanf("%lf%lf%lf%lf", &st.x, &st.y, &ed.x, &ed.y);
}
Segment() {}
Segment(Point A, Point B) { st = A; ed = B; }
};
bool IsSegmentIntersect(const Segment &A,const Segment &B) {
return IsSegmentIntersect(A.st, A.ed, B.st, B.ed);
}
// Ax + By + C = 0
// y = kX + b; 使用克拉默法则的时候需要注意C的符号.
struct Line {
Point st;
Point ed;
double A, B, C, k, b;
// Vector v;
void Read() {
scanf("%lf%lf%lf%lf", &st.x, &st.y, &ed.x, &ed.y);
init();
// printf("%lf %lf %lf \n", A, B, C);
}
Line () {}
Line (Point A, Point B) {
st = A;
ed = B;
init();
}
Line (Segment s_g) {
st = s_g.st;
ed = s_g.ed;
init();
}
void init() {
if (ed.x - st.x == 0.0) {
k = 999999999999.99;
b = st.y;
} else {
k = (ed.y - st.y) / (ed.x - st.x);
b = st.y - k * st.x;
}
A = st.y - ed.y;
B = ed.x - st.x;
C = st.x * (ed.y - st.y) - st.y * (ed.x - st.x);
// v = ed - st;
}
};
// 判断两直线是否共线
bool IsLineLineCollinear(const Line &a,const Line &b) {
return fabs(((a.ed - a.st) ^ (b.ed - b.st))) < eps ? true : false;
}
// 判断两直线是否平行
bool IsLineLineParallel(const Line &a, const Line &b) {
if (!IsLineLineCollinear(a, b)) return false;
return fabs(((b.st - a.st) ^ (b.ed - a.st))) < eps ? false : true;
}
// 判断两直线是否重叠
bool IsLineLineOver(const Line &a, const Line &b) {
if (!IsLineLineCollinear(a, b)) return false;
return fabs(((b.st - a.st) ^ (b.ed - a.st))) < eps ? true : false;
}
// 使用前需 确保直线已经调用 init()初始化, 是根据已知直线2点来求的.
// 使用克拉默法则
Point GetLineLineIntersect(const Line &a, const Line &b) {
Point pt = a.st;
if (IsLineLineCollinear(a, b)) return pt; // 如果共线 或者平行 瞎返回
double D = a.A * b.B - a.B * b.A;
double D1 = (-a.C) * b.B - a.B * (-b.C);
double D2 = a.A * (-b.C) - b.A * (-a.C);
pt.x = D1 / D;
pt.y = D2 / D;
return pt;
}
// 跨立实验的原理
// 如果直线AB 跨立 CD 那么有 (AC ^ AB) * (AD ^ AB) > 0
// 所以跨立实验要判2个GetCross
bool IsLineSegmentIntersect(const Line &line, const Segment &s_g) {
if (GetCross(line.st, s_g.st, line.ed) * GetCross(line.st, s_g.ed, line.ed) > 0.0 ) return true;
return false;
}
// 叉积极角排序.
// 以Qcmppt为起点 做极角排序.
Point Qcmppt;
bool cmp_j_j_j(const Point &A, const Point &B) {
int m = (A-Qcmppt) ^ (B-Qcmppt);
if (m != 0.0) return m > 0.0;
if (A.x != B.x) return A.x < B.x;
return false;
}
// 求多边形面积, 从pts从1开始求面积 有n个点
// 按照极角排序
double GetArea(Point pts[], int ss[], int n)
{
int i;
double res = 0;
// 只划分成n-2个三角形.
for (i=2; i<n; ++i)
res += GetCross(pts[ss[1]], pts[ss[i]], pts[ss[i+1]]);
res = res / 2.0;
return fabs(res);
}
// 求多边形周长 也需要先排序.
double GetLenght(Point pts[], int ss[], int n)
{
int i;
double res = GetLenght(pts[ss[1]], pts[ss[n]]);
// 只划分成n-2个三角形.
for (i=1; i<n; ++i)
res += GetLenght(pts[ss[i]], pts[ss[i+1]]);
return fabs(res);
}