已知直线上两点坐标,求直线方程
已知两点(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;
}