码迷,mamicode.com
首页 > 其他好文 > 详细

ACM 常用几何基本数学公式

时间:2016-07-15 06:41:28      阅读:182      评论:0      收藏:0      [点我收藏+]

标签:

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.   判断三点共线

判断三点共线的方法可以通过求斜率,求周长,求面积来判断。

  1.  通过斜率来判断:(ay-by)/(ax-bx) == (cy-by)/(cx-bx)需要比较判断分母不为0 的情况,而且还可能出现除法精度丢失。
  2. 通过周长来判断:若AC > AB>BC,判断 AC == AB+BC,求距离的时候会用到sqrt,丢失精度。
  3. 通过面积来判断由上述的叉积求面积法来判断,面积为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/acmore_xiong/article/details/51899284

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!