简单的平面几何问题

已知直线上两点坐标,求直线方程

已知两点(x1,y1) ,(x2,y2),代入两点公式:
(x-x1)/(x2-x1)=(y-y1)/(y2-y1)
于是有:

ax+by+c=0;
//a=y2-y1
//b=x1-x2
//c=x2*y1-x1*y2

判断两点是否在直线的同一侧

方法1:

如果已知直线方程为:

a*x+b*y+c=0

则直接把两点(x1,y1) ,(x2,y2)代入方程,判断

(a*x1+b*y1+c) * (a*x2+b*y2+c) > 0

即是否同号,如果同号,说明两个点在直线的同一侧;如果异号,说明分别在两侧。


方法2:利用叉积(向量积)

这里写图片描述

注意这里的直线是有方向的,例如P点在AB的左边,但在BA的右边。

两个向量的叉积是这样定义的: 向量积 a x b = (^n) * |a| * |b| * sin<a, b>, 其中^n是同时垂直于a/b且符合右手定则的单位向量。

这里写图片描述

如果我们把^n忽略,只取数值的结果,AB × AP = |AB| * |AP| * sin∠PAB。P在AB的左边,则∠PAB在0°到180°之间 sin∠PAB > 0 P在AB右边时,则∠PAB在-180°到0°之间 sin∠PAB < 0 因此,我们只要用AB和AP的叉积的正负,就可以判断P和AB的相对位置(AP相对AB是顺时针还是逆时针旋转)。

记每个点包含x坐标和y坐标:

struct Point{
    int x;
    int y;
    Point(int a, int b) :x(a), y(b){ }
};

则AB向量的坐标是(b.x-a.x, b.y-a.y) ,AP向量的坐标是(p.x-a.x, p.y-a.y),则

AB × AP = (b.x – a.x)(p.y – a.y) – (b.y – a.y)(p.x – a.x)

这样就得到了计算叉积的函数:

int cross(const Point &a, const Point &b, const Point &p)
{
    return (b.x - a.x)*(p.y - a.y) - (b.y - a.y)*(p.x - a.x);
}

利用叉积的正负号判断点和直线位置关系,看P是否在AB的左边:

bool toLeft(const Point &a, const Point &b, const Point &p)
{
    return cross(a, b, p) > 0;
}

已知三角形三点坐标,求三角形面积

设三角形的三个顶点为:A(x1,y1),B(x2,y2),C(x3,y3)
则其面积的公式为:

S=(1/2)*(x1y2+x2y3+x3y1-x1y3-x2y1-x3y2)

已知三角形三条边长,求三角形面积

设三角形的三条边长分别为a、b、c,根据海伦公式,有其面积的公式为:

p=(a+b+c)/2 //p为半周长
S=sqrt( p*(p-a)*(p-b)*(p-c) )

判断两个矩形是否重叠

判断两个圆是否有重叠很简单,当且仅当 r1 + r2 > d 时,两个圆有重叠部分。
矩形可以看成是特殊的圆,一个矩形存在垂直方向和水平方向两个半径v和h。分别求矩形中心c1和c2的垂直和水平距离,只有当垂直和水平方向上都大于等于对应半径之和时,两个矩形才不重叠。

用叉积判断点是否在三角形内部

这里写图片描述

紧接着上面利用叉积判断点在直线哪一侧的思路,如果点在三角形的内部,沿着三边走一圈,这个点相对于行进路径始终保持相同方向(图中一直在左边); 如果点在三角形的外部,沿着三条边走一圈,会有不同的结果(图中左右左)。 这样,只要判断点和直线的相对位置就可以了。

对三角形的三边判断三次,考察p是否在三条边的同一侧。这样就可以得到点是否在三角形内部了。
注意这里还判断了一下,输入的ABC是否是在一条直线上。

bool inTriangle(const Point &p, const Point &a, const Point &b, const Point &c)
{
    bool res = toLeft(a, b, p);
    if (res != toLeft(b, c, p))
        return false;
    if (res != toLeft(c, a, p))
        return false;
    //如果ABC在一条直线
    if (cross(a, b, c) == 0)
        return false;
    return true;
}

用叉积判断点是否在矩形内部

和上面又是很类似,只需要判断该点是否在矩形的上下两条边和左右两条边之间就行。
判断一个点P是否在两条直线之间,可以转换成:用两条直线的同一方向的向量和点P做叉乘,如果两个叉乘异号,则说明P在直线之间。

例如:矩形四个顶点P1,P2,P3,P4,判断P是否包含在矩形中,只需要判断:

|P2P|×|P1P2|*|P3P|×|P3P4|<=0 And |P1P|×|P1P4|*|P2P|×|P2P3|<=0

最小圆覆盖问题

平面上n个点,求一个半径最小的圆,能够覆盖所有的点。
思路
假设圆O是由前i-1个点得到的最小覆盖圆,加入第i个点,如果它在圆内或边上则什么也不做。否则,新得到的最小覆盖圆肯定经过第i个点。
然后以第i个点为基础(半径为0),重复以上过程依次加入第j个点,若第j个点在圆外,则最小覆盖圆必经过第j个点。
重复以上步骤(因为最多需要三个点来确定这个最小覆盖圆,所以重复三次)。遍历完所有点之后,所得到的圆就是覆盖所有点得到的最小圆。
代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<algorithm> 
using namespace std;

struct Point{
    double x,y; 
}p[505];

double dis(const Point &a,const Point &b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); 
}
//返回由3个点确定的圆的圆心
Point circumcenter(const Point &a,const Point &b,const Point &c)
{ //返回三角形的外心 
    Point ret; 
    double a1=b.x-a.x,b1=b.y-a.y,c1=(a1*a1+b1*b1)/2;
    double a2=c.x-a.x,b2=c.y-a.y,c2=(a2*a2+b2*b2)/2;
    double d=a1*b2-a2*b1;
    ret.x=a.x+(c1*b2-c2*b1)/d;
    ret.y=a.y+(a1*c2-a2*c1)/d;
    return ret; 
}

//求最小覆盖圆的圆心和半径
void min_cover_circle(Point *p,int n,Point &c,double &r)
{
    random_shuffle(p,p+n);
    c=p[0]; 
    r=0;
    for(int i=1;i<n;i++)
    {
        //确定在当前最小圆外的第一个点
        if(dis(p[i],c)>r)  
        { 
            c=p[i]; 
            r=0;
            for(int j=0;j<i;j++)
                //确定第二个点
                if(dis(p[j],c)>r)
                {
                    c.x=(p[i].x+p[j].x)/2;
                    c.y=(p[i].y+p[j].y)/2;
                    r=dis(p[j],c);
                    for(int k=0;k<j;k++)
                        //确定第三个点
                        if(dis(p[k],c)>r)
                        {
                            //求外接圆圆心,三点必不共线 
                            c=circumcenter(p[i],p[j],p[k]); 
                            r=dis(p[i],c); 
                        } 
                }  
        }    
    } 
} 
int main()
{
    int n;
    Point c;
    double r; 
    while(scanf("%d",&n)==1 && n){
        for(int i=0;i<n;i++)
            scanf("%lf%lf",&p[i].x,&p[i].y);
        min_cover_circle(p,n,c,r);                    
        printf("%.2lf %.2lf %.2lf\n",c.x,c.y,r);                   
    } 
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值