ACM常用几何基本数学公式
1. 海伦公式求面积
公式描述:公式中a,b,c分别为三角形三边长,p为半周长,S为三角形的面积。
2. 矢量向量求面积
3. 点到直线的距离公式
方法一:距离公式直接求
公式描述:公式中的直线方程为Ax+By+C=0,点P的坐标为(x0,y0)。但是直线方程不是够直接。推荐使用方法二。
方法二:先用海伦公式求面积然后求三角形高
4. 点到线段的距离公式[或:点到线段最近的点]
有以下四种情况:
- 点在线段上,距离为0;
- 线段是一个点,用两点公式求;
- 三点构成直角三角形或者钝角三角形,那么直角或者钝角即为点到线段最近的点;
- 三点构成锐角三角形,那么距离为三角形的高,点到线段最近的点。
以下是具体代码, 代码已经在 51 Nod 1298 圆与三角形 测试过了。
#include <cmath> #include <queue> #include <vector> #include <cstdio> #include <string> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> using namespace std; //#pragma comment(linker, "/STACK:1024000000,1024000000") #define FIN freopen("input.txt","r",stdin) #define FOUT freopen("output.txt","w",stdout) #define fst first #define snd second typedef __int64 LL; //typedef long long LL; typedef unsigned int uint; typedef pair<int, int> PII; const int INF = 0x3f3f3f3f; const double eps = 1e-6; const int MAXN = 3 + 5; const int MAXM = 600 + 5; // 判断浮点数与0的大小关系 int sgn(double x) { if (fabs(x) < eps) return 0; if (x < 0) return -1; else return 1; } struct Point { double x, y; Point() {} Point(double _x, double _y) : x(_x), y(_y) {} } pc, p[MAXN]; typedef Point Vect; /** * 两点距离公式 * */ double getDist(const Point &p1, const Point &p2) { int tx = p1.x - p2.x; int ty = p1.y - p2.y; return sqrt(tx * tx + ty * ty); } /** * 由两点求向量 * */ Vect getVect(const Point& p1, const Point &p2) { return Vect(p2.x - p1.x, p2.y - p1.y); } /** * 求矢量叉积 * @param v1 [description] * @param v2 [description] * @return [description] */ double xmult(const Vect& v1, const Vect& v2) { return v1.x * v2.y - v2.x * v1.y; } /** * 矢量叉积求面积 * */ double getArea1(const Point &p0, const Point &p1, const Point &p2) { Vect v1 = getVect(p0, p1); Vect v2 = getVect(p0, p2); return 0.5 * getVectProduct(v1, v2); } /** * 海伦公式求面积 * */ double getArea2(const Point &p0, const Point &p1, const Point &p2) { double p0p1 = getDist(p0, p1); double p0p2 = getDist(p0, p2); double p1p2 = getDist(p1, p2); double x = (p0p1 + p0p2 + p1p2) / 2.0; return sqrt(x * (x - p0p1) * (x - p0p2) * (x - p1p2)); } /** * 利用海伦公式或者叉积公式求点到直线的距离 * @param p0 [点] * @param p1 [直线上的点1] * @param p2 [直线上的点2] * @return [点到直线的距离] */ double point2line(const Point &p0, const Point &p1, const Point &p2) { double area = getArea1(p0, p1, p2); // double area = getArea2(p0, p1, p2); double p1p2 = getDist(p1, p2); return 2 * area / p1p2; } /** * 获取点到线段的最小距离 * @param p0 [点] * @param p1 [线段端点1] * @param p2 [线段端点2] * @return [点到线段的距离] */ double point2lineSeg_Near(const Point &p0, const Point &p1, const Point &p2) { double p0p1 = getDist(p0, p1); double p0p2 = getDist(p0, p2); double p1p2 = getDist(p1, p2); // 点在线段上 if (sgn(p0p1 + p0p2 - p1p2) == 0) return 0; // 线段两个端点p1,p2重合 if (sgn(p1p2) == 0) return p0p1; // ∠p0p1p2 为直角或者钝角 if (p0p2 * p0p2 >= p0p1 * p0p1 + p1p2 * p1p2) return p0p1; // ∠p0p2p1 为直角或者钝角 if (p0p1 * p0p1 >= p0p2 * p0p2 + p1p2 * p1p2) return p0p2; // ∠p0p1p2 和 ∠p0p2p1 都是锐角,等价于求点到直线的距离 return point2line(p0, p1, p2); } /** * 求点到线段的最长距离 * @param p0 [点] * @param p1 [线段端点1] * @param p2 [线段端点2] * @return [最长距离] */ double point2lineSeg_Far(const Point &p0, const Point &p1, const Point &p2) { double p0p1 = getDist(p0, p1); double p0p2 = getDist(p0, p2); return max(p0p1, p0p2); } int T; double R; int main() { #ifndef ONLINE_JUDGE FIN; #endif // ONLINE_JUDGE scanf("%d", &T); while (T --) { scanf("%lf %lf %lf", &pc.x, &pc.y, &R); for (int i = 0; i < 3; i ++) { scanf("%lf %lf", &p[i].x, &p[i].y); } double mi[3], ma[3]; mi[0] = point2lineSeg_Near(pc, p[0], p[1]); mi[1] = point2lineSeg_Near(pc, p[0], p[2]); mi[2] = point2lineSeg_Near(pc, p[1], p[2]); ma[0] = point2lineSeg_Far(pc, p[0], p[1]); ma[1] = point2lineSeg_Far(pc, p[0], p[2]); ma[2] = point2lineSeg_Far(pc, p[1], p[2]); bool suc = false; for (int i = 0; i < 3; i ++) { if (sgn(mi[i] - R) <= 0 && sgn(ma[i] - R) >= 0) { suc = true; break; } } puts(suc ? "Yes" : "No"); } return 0; }
5. 判断三点共线
判断三点共线的方法可以通过求斜率,求周长,求面积来判断。
- 通过斜率来判断:(ay-by)/(ax-bx) == (cy-by)/(cx-bx)需要比较判断分母不为0 的情况,而且还可能出现除法精度丢失。
- 通过周长来判断:若AC > AB>BC,判断 AC == AB+BC,求距离的时候会用到sqrt,丢失精度。
- 通过面积来判断:由上述的叉积求面积法来判断,面积为0,证明三点共线。
故,只需要判断即可。
6.判断四点是否共面
类似上面的方法, 可以根据体积来判断。 四个点构成三个向量, 如果这三个向量的混合积为0,那么体积为0,那么四点共面。
代码如下【包括了三维空间的点积、叉积、混合积的计算】,代码已经在 51Nod 1265四点共面 中测试过了。
#include <cmath> #include <queue> #include <vector> #include <cstdio> #include <string> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> using namespace std; //#pragma comment(linker, "/STACK:1024000000,1024000000") #define FIN freopen("input.txt","r",stdin) #define FOUT freopen("output.txt","w",stdout) #define fst first #define snd second typedef __int64 LL; //typedef long long LL; typedef unsigned int uint; typedef pair<int, int> PII; const int INF = 0x3f3f3f3f; const double eps = 1e-6; const int MAXN = 4 + 5; const int MAXM = 600 + 5; int sgn(double x) { if (fabs(x) < eps) return 0; if (x < 0) return -1; else return 1; } struct Point { double x, y, z; }; typedef Point Vect; int T; Point pnts[MAXN]; /** * 三维空间中两点求向量 * */ Vect getVect(const Point& p1, const Point& p2) { Vect v; v.x = p1.x - p2.x; v.y = p1.y - p2.y; v.z = p1.z - p2.z; return v; } /** * 求三维空间的两向量叉积 (v1 × v2) * */ Vect xMulti(const Vect& v1, const Vect& v2) { Vect v; v.x = v1.y * v2.z - v1.z * v2.y; v.y = v1.z * v2.x - v1.x * v2.z; v.z = v1.x * v2.y - v1.y * v2.x; return v; } /** * 求三维空间中的两向量点积 (v1 · v2) * */ double dotMulti(const Vect& v1, const Vect& v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } /** * 求三维空间中三个向量的混合积 (v1 × v2)·(v3) * */ double mixMulti(const Vect& v1, const Vect& v2, const Vect& v3) { return dotMulti(xMulti(v1, v2), v3); } /** * 判断四点是否共面 * 根据混合积求体积,判断体积是否为0 */ bool isInArea(const Point& p1, const Point& p2, const Point& p3, const Point& p4) { Vect p1p2, p1p3, p1p4; p1p2 = getVect(p1, p2); p1p3 = getVect(p1, p3); p1p4 = getVect(p1, p4); return sgn(mixMulti(p1p2, p1p3, p1p4)) == 0; } int main() { #ifndef ONLINE_JUDGE FIN; #endif // ONLINE_JUDGE scanf("%d", &T); while (T --) { for (int i = 0; i < 4; i ++) { scanf("%lf %lf %lf", &pnts[i].x, &pnts[i].y, &pnts[i].z); } bool ret = isInArea(pnts[0], pnts[1], pnts[2], pnts[3]); puts(ret ? "Yes" : "No"); } return 0; }
6.判断线段是否相交
代码已经在 51 nod 1264 线段相交 测试过了
#include <cmath> #include <queue> #include <vector> #include <cstdio> #include <string> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> using namespace std; //#pragma comment(linker, "/STACK:1024000000,1024000000") #define FIN freopen("input.txt","r",stdin) #define FOUT freopen("output.txt","w",stdout) #define fst first #define snd second typedef __int64 LL; //typedef long long LL; typedef unsigned int uint; typedef pair<int, int> PII; const int INF = 0x3f3f3f3f; const double eps = 1e-6; const int MAXN = 4 + 5; const int MAXM = 600 + 5; int sgn(double x) { if (fabs(x) < eps) return 0; if (x < 0) return -1; else return 1; } struct Point { double x, y; Point() {} Point(double _x, double _y) : x(_x), y(_y) {} } pnts[MAXN]; typedef Point Vect; Vect getVect(const Point& p1, const Point &p2) { return Vect(p2.x - p1.x, p2.y - p1.y); } double xMulti(const Vect& v1, const Vect& v2) { return v1.x * v2.y - v2.x * v1.y; } /** * 判断线段是否相交 * */ bool segInter(const Point& p1, const Point& p2, const Point& p3, const Point& p4) { return max(p1.x, p2.x) >= min(p3.x, p4.x) && max(p3.x, p4.x) >= min(p1.x, p2.x) && max(p1.y, p2.y) >= min(p3.y, p4.y) && max(p3.y, p4.y) >= min(p1.y, p2.y) && sgn(xMulti(getVect(p3, p2), getVect(p1, p2))) * sgn(xMulti(getVect(p4, p2), getVect(p1, p2))) <= 0 && sgn(xMulti(getVect(p1, p4), getVect(p3, p4))) * sgn(xMulti(getVect(p2, p4), getVect(p3, p4))) <= 0; } int T; int main() { #ifndef ONLINE_JUDGE FIN; #endif // ONLINE_JUDGE scanf("%d", &T); while(T --) { for(int i = 0; i < 4; i ++) { scanf("%lf %lf", &pnts[i].x, &pnts[i].y); } bool ret = segInter(pnts[0], pnts[1], pnts[2], pnts[3]); puts(ret ? "Yes" : "No"); } return 0; }
补充链接:
- ACM常用数学公式汇总:http://blog.csdn.net/qq_26891045/article/details/51490709
- 给定4个点坐标求四面体体积: http://blog.csdn.net/foreverlin1204/article/details/6124184
时间: 2024-10-10 10:17:59