参考博客:http://dev.gameres.com/Program/Abstract/Geometry.htm#

向量的点积

结果为

点积的结果是一个数值。

点积的集合意义:我们以向量 a 向向量 b 做垂线,则为 a 在向量 b 上的投影,即点积是一个向量在另一个向量上的投影乘以另一个向量。且满足交换律

应用:可以根据集合意义求两向量的夹角,

向量的叉积

结果为

叉积的结果也是一个向量,是垂直于向量a,b所形成的平面,如果看成三维坐标的话是在 z 轴上,上面结果是它的模。

方向判定:右手定则,(右手半握,大拇指垂直向上,四指右向量a握向b,大拇指的方向就是叉积的方向)

坐标运算:

,i,j,k分别是X,Y,Z轴方向的单位向量,则,

为了帮助记忆,利用三阶

行列式,写成

det img

叉积的集合意义:

1:其结果是a和b为相邻边形成平行四边形的面积。

2:结果有正有负,有sin(a,b)可知和其夹角有关,夹角大于180°为负值。

3:叉积不满足交换律

应用:

1:通过结果的正负判断两矢量之间的顺逆时针关系

若 a×b > 0表示a在b的顺时针方向上

若 a×b < 0表示a在b的逆时针方向上

若 a×b == 0表示a在b共线,但不确定方向是否相同

2:判断折线拐向,可转化为判断第三点在前两的形成直线的顺逆时针方向,然后判断拐向。

3:判断一个点在一条直线的哪一侧,同样上面的方法。

4:判断点是否在线段上,可利用叉乘首先判断是否共线,然后在判断是否在其上。

5:判断两条直线是否相交

​ 我们分两步确定两条线段是否相交:

​ (1)快速排斥试验

​ 设以线段 P1P2 为对角线的矩形为R, 设以线段 Q1Q2 为对角线的矩形为T,如果R和T不相交,显然两线段不会相交。

  (2)跨立试验
  如果两线段相交,则两线段必然相互跨立对方。若P1P2跨立Q1Q2 ,则矢量 ( P1 - Q1 ) 和( P2 - Q1 )位于矢量( Q2 - Q1 ) 的两侧,即( P1 - Q1 ) × ( Q2 - Q1 ) ( P2 - Q1 ) × ( Q2 - Q1 ) < 0。上式可改写成( P1 - Q1 ) × ( Q2 - Q1 ) ( Q2 - Q1 ) × ( P2 - Q1 ) > 0。当 ( P1 - Q1 ) × ( Q2 - Q1 ) = 0 时,说明 ( P1 - Q1 ) 和 ( Q2 - Q1 )共线,但是因为已经通过快速排斥试验,所以 P1 一定在线段 Q1Q2上;同理,( Q2 - Q1 ) ×(P2 - Q1 ) = 0 说明 P2 一定在线段 Q1Q2上。所以判断P1P2跨立Q1Q2的依据是:( P1 - Q1 ) × ( Q2 - Q1 ) ( Q2 - Q1 ) × ( P2 - Q1 ) >= 0。同理判断Q1Q2跨立P1P2的依据是:( Q1 - P1 ) × ( P2 - P1 ) ( P2 - P1 ) × ( Q2 - P1 ) >= 0。

具体情况如下图所示:

img

//这里直线1的两个顶点是p[0],p[1];直线2的两个顶点是p[2],p[3]。
struct point
{
    double x,y;
}p[5];
double calc(point a,point b,point c)
{
    return a.x*b.y+b.x*c.y+c.x*a.y-a.x*c.y-b.x*a.y-c.x*b.y;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        for(int i=0;i<4;i++)
            scanf("%lf%lf",&p[i].x,&p[i].y);
        double l0102=calc(p[0],p[1],p[2]);
        double l0103=calc(p[0],p[1],p[3]);
        double l2320=calc(p[2],p[3],p[0]);
        double l2321=calc(p[2],p[3],p[1]);
        if(l0102*l0103<=0&&l2320*l2321<=0)printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}
混合积

设 a ,b,c 是空间中三个向量,则 (a×b)·c称为三个向量 a ,b ,c 的混合积,记作[a b c] 或 (a,b,c) 或 (abc)。

。则有

img

应用:

四个点构成三个向量, 如果这三个向量的混合积为0,那么体积为0,那么四点共面。

求任意多边形面积-有向面积

只要记住这个公式:

img

如果逆时针给出点坐标,值为正,

如果顺时针给出点坐标,值为负。

当i=n-1 i+1就是n所代表的点就是第一个点。

struct point
{
    int x,y;
}p[110];
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)break;
        for(int i=0;i<n;i++)
            scanf("%d%d",&p[i].x,&p[i].y);
        p[n].x=p[0].x;p[n].y=p[0].y;
        double ans=0;
        for(int i=0;i<n;i++)
            ans+=0.5*(p[i].x*p[i+1].y-p[i+1].x*p[i].y);
        printf("%.1f\n",ans);
    }
    return 0;
}
皮克公式

给定顶点坐标均是整点(或正方形格点)的简单多边形,皮克定理说明了其面积S和内部格点数目n、边上格点数目s的关系:

img

(其中n表示多边形内部的点数,s表示多边形边界上的点数,S表示多边形的面积)

poj 1265

题意:给出机器人每一步向右走和向上走的步数,得到一个平面上的简单多边形,求边上的点,多边形内的点,多边形面积。

思路:

先求边上格点数目:已知一条直线,如(5,0)到(6,3)这条直线,经过的整点数是2,可通过如下公式计算:

格点数 = gcd(|x2-x1|,|y2-y1|)

前提,约定起点不算在该线所经过格点数中。

再利用上面求多边形面积的方法求出面积,根据皮克公式可以求出内部格点数目。

struct point
{
    int x,y;
}p[110];
int n;
int gcd(int a,int b)
{
    if(a==0)return b;
    if(b==0)return a;
    if(a<b){int temp;temp=a;a=b;b=temp;}
    while(a%b){int r=a%b;a=b;b=r;}
    return b;
}
double mianji()
{
    double s=0;
    for(int i=0;i<n;i++)
        s+=0.5*(p[i].x*p[i+1].y-p[i+1].x*p[i].y);
    return abs(s);
}
int main()
{
    int t;
    scanf("%d",&t);
    for(int kase=1;kase<=t;kase++)
    {
        scanf("%d",&n);
        int num=0;//边上的格点数
        p[0].x=0;p[0].y=0;
        for(int i=1;i<=n;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            num+=gcd(abs(a),abs(b));
            p[i].x=p[i-1].x+a;
            p[i].y=p[i-1].y+b;
        }
        double ss=mianji();
        printf("Scenario #%d:\n%d %d %.1f\n\n",kase,(int)ss-(num/2)+1,num,ss);
    }
    return 0;
}
精度

参考博客:http://www.cnblogs.com/crazyacking/p/4668471.html

判断浮点数相等:

#define eps 1e-8//依题目而定
int sgn(double x)
{
    if(fabs(x) < eps)return 0;
    if(x < 0)return -1;
    else return 1;
}
凸包

参考博客:https://www.cnblogs.com/aiguona/p/7232243.html

1⃣把纵坐标最小的点作为p0,如果有多个纵坐标最小的点,就选其中横坐标最小的点。

2⃣按逆时针方向相对于p0进行极角排序,如果极角相同,就把距离p0近的排在前面。

3⃣把p0,p1入栈,把p2作为当前点。

4⃣栈顶的上一个到栈顶画一根直线,如果当前点在这根直线的左边或者直线上,该点入栈,如果当前点在这根直线的右边,就让栈顶元素出栈,继续4⃣步骤。

5⃣如果不是最后一个元素,选这个入栈元素后面一个元素为当前点,继续4⃣步骤。

以hdu1392求凸包周长为例(这里注意n=1和n=2要特判(因为没有形成圈))

struct point
{
    double x,y;
}a[110],p[110];
int n,top;
double cross(point p0,point p1,point p2)//叉乘判断在左边还是右边
{
    return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
double dis(point a,point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool cmp(point p1,point p2)
{
    double c=cross(a[0],p1,p2);
    if(c>0||(c==0&&dis(a[0],p1)<dis(a[0],p2)))
        return 1;
    return 0;
}
void graham()
{
    int pos=0;//最下最左的点
    for(int i=0;i<n;i++)
        if((a[i].y<a[pos].y)||(a[i].y==a[pos].y&&a[i].x<a[pos].x))
            pos=i;
    swap(a[pos],a[0]);
    sort(a+1,a+n,cmp);//极角排序
    top=1;
    p[0]=a[0];
    p[1]=a[1];//0,1入栈
    for(int i=2;i<n;i++)
    {
        while(top&&cross(p[top-1],p[top],a[i])<0)//栈顶和栈顶的前一个连线,如果当前点在右边的话,栈顶出栈,继续
            top--;
        top++;
        p[top]=a[i];
    }
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)break;
        if(n==1){printf("0.00\n");continue;}
        for(int i=0;i<n;i++)
            scanf("%lf%lf",&a[i].x,&a[i].y);
        if(n==2){printf("%.2f\n",dis(a[0],a[1]));continue;}//因为不成圈所以2要特判
        graham();
        double ans=0;
        p[top+1]=p[0];
        for(int i=0;i<=top;i++)
            ans+=dis(p[i],p[i+1]);
        printf("%.2f\n",ans);
    }
    return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

Wannafly挑战赛9 Previous
树形dp Next