二维几何基础

模板(from 紫书):

#include<bits/stdc++.h>
using namespace std;
typedef long double ld;
const ld eps=1e-10;
const ld PI=acos((ld)(-1.0));
struct Point
{
    ld x,y;
    Point(ld x=0,ld y=0) : x(x),y(y) {}
};
typedef Point Vector;
Vector operator + (Vector a, Vector b) { return Vector(a.x+b.x, a.y+b.y); }
Vector operator - (Vector a, Vector b) { return Vector(a.x-b.x, a.y-b.y); }
Vector operator * (Vector a, ld p) { return Vector(a.x*p, a.y*p); }
Vector operator / (Vector a, ld p) { return Vector(a.x/p, a.y/p); }
bool operator < (const Point& a, const Point &b)
{
    return a.x<b.x||(a.x==b.x&&a.y<b.y) ;
}
int dcmp(ld x)
{
    if(fabs(x)<eps) return 0; else return x<0 ? -1 : 1;
}
bool operator == (const Point &a, const Point &b)
{
    return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}
// Vector a; 的极角是 atan2(a.y,a.x);
ld operator * (Vector a, Vector b) { return a.x*b.x+a.y*b.y; }
ld operator ^ (Vector a, Vector b) { return a.x*b.y-b.x*a.y; }
ld length(Vector a) { return sqrt(a*a); }
ld angle(Vector a, Vector b) { return acos(a*b/length(a)/length(b)); }
ld area3(Point a, Point b, Point c) { return (b-a)^(c-a); }
Vector rotate(Vector a, ld rad) { return Vector(a.x*cos(rad)-a.y*sin(rad), a.x*sin(rad)+a.y*cos(rad)); }
Vector Normal(Vector a) { ld L = length(a); return Vector(-a.y/L,a.x/L); }

//直线(P+tv)和(Q+tw)的交点
//调用前用求两条直线有唯一交点,当且仅当v^w!=0 。
Point getLineInserction(Point P, Vector v, Point Q, Vector w)
{
    Vector u = P-Q;
    ld t=(w^u)/(v^w);
    return P+v*t;
}
//求P到直线AB的距离,A和B是直线上的任意两点。
ld distanceToLine(Point P, Point A, Point B)
{
    Vector v1 = B-A, v2 = P-A;
    return fabs(v1^v2) / length(v1) ;
}
//求P到线段AB的距离,A和B是线段的端点。
ld distanceToSegment(Point P, Point A, Point B)
{
    if(A==B) return length(P-A);
    Vector v1=B-A,v2=P-A,v3=P-B;
    if(dcmp(v1*v2)<0) return length(v2);
    else if(dcmp(v1*v3)>0) return length(v3);
    else return fabs(v1^v2) / length(v1);
}
//求出P在直线AB上的投影
Point getLineProjection(Point P, Point A, Point B)
{
    Vector v=B-A;
    return A+v*((v*(P-A))/(v*v));
}
// 判断线段a1a2与线段b1b2是否相交
bool isSegmentProperIntersection(Point a1, Point a2, Point b1, Point b2)
{
    ld c1 = (a2-a1)^(b1-a1), c2=(a2-a1)^(b2-a1),
       c3 = (b2-b1)^(a1-b1), c4=(b2-b1)^(a2-b1);
    return dcmp(c1)*dcmp(c2)<0&&dcmp(c3)*dcmp(c4)<0;
}
//判断点是否在一条线段上
bool onSegment(Point p, Point a1, Point a2)
{
    return dcmp((a1-p)^(a2-p))==0&&dcmp((a1-p)*(a2-p)) < 0;
}
int main()
{

    return 0;
}

例题:
UVA 11178 Morley定理:
用到了直线旋转,直线交点和直线夹角


Point A,B,C;
Point solve(Point A, Point B, Point C)
{
    ld radABC=angle(A-B,C-B),radACB=angle(A-C,B-C);
    Vector BD=rotate(C-B,radABC/3);
    Vector CD=rotate(A-C,radACB*2/3);
    return getLineInserction(B,BD,C,CD);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int xa,ya,xb,yb,xc,yc;
        scanf("%d%d%d%d%d%d",&xa,&ya,&xb,&yb,&xc,&yc);
        A=Point(xa,ya);
        B=Point(xb,yb);
        C=Point(xc,yc);
        Point D=solve(A,B,C);
        Point E=solve(B,C,A);
        Point F=solve(C,A,B);
        printf("%.15f %.15f %.15f %.15f %.15f %.15f\n",(double)D.x,(double)D.y,(double)E.x,(double)E.y,(double)F.x,(double)F.y);
    }
    return 0;
}

LA 3263 :一笔画。
这里有一个常用的公式叫做欧拉定理 :
在平面图(在图论中,平面图是可以画在平面上并且使得不同的边可以互不交叠的图。而如果一个图无论怎样都无法画在平面上,并使得不同的边互不交叠,那么这样的图不是平面图,或者称为非平面图。)上, V+FE=2 ,其中 V 是顶点数,F是面数, E 是边数。

所以这题求出顶点数和面数就行了。顶点数分为原有的点和相交生成的点,相交生成的点可以在沿着笔画的时候判断是否与之前的边相交就行了。这部分复杂度是 O(n2)
边数可以枚举每条边,然后枚举每个点,如果点在边上,那么边数+1。复杂度为 O(n3)


const int N=307;
Point p[N],v[N*N];
int main()
{
    int n,kase=1;
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        for(int i=0;i<n;++i)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            v[i]=p[i]=Point(x,y);
        }
        --n;
        int c=n,e=n;
        for(int i=0;i<n;++i)
            for(int j=i-2;j>=0;--j)
                if(isSegmentProperIntersection(p[i],p[i+1],p[j],p[j+1]))
                    v[c++]=getLineIntersection(p[i],p[i+1]-p[i],p[j],p[j+1]-p[j]);
        sort(v,v+c);
        c=unique(v,v+c)-v;
        for(int i=0;i<n;++i)
            for(int j=0;j<c;++j)
                if(onSegment(v[j],p[i],p[i+1])) ++e;
        printf("Case %d: There are %d pieces.\n",kase++,2+e-c);


    }
    return 0;
}

UVA 11796 :狗的距离。
题意是给定两条折线,两条狗分别从起点开始匀速跑,到终点的时间一样。要求两条狗的最大距离减去最小距离的差值。
假如狗在两条线段上跑,假设一条狗静止不动,另一条狗的相对运动形成一条线段,很容易算出点到线段的距离:
运动轨迹

假设A和B是A狗和B狗走的线段。可以看出两条红线是起始AB相对位置向量和终点AB相对位置向量。后面那条红线可以用子线描述,所以B的相对运动路径为Sb->Sb+B-A。因此就能算了。

分段
然后扩展到这题。将每次到拐点前的线段画出来,然后用上述过程算就行了。上图颜色相同的部分是要一起计算的段。复杂度是 O(2n)


int T,n,m;
Point pa[57],pb[57];
ld MAX,MIN;
Point read_point()
{
    int x,y;
    scanf("%d%d",&x,&y);
    return Point(x,y);
}
void update(Point P, Point A, Point B)
{
//    cout << P.x << " " << P.y << " " << A.x << " " << A.y << " " << B.x << " " << B.y <<endl;
    MIN=min(MIN,distanceToSegment(P,A,B));
    MAX=max(MAX,length(P-A));
    MAX=max(MAX,length(P-B));
}
int main()
{
    scanf("%d",&T);
    int kase=1;
    while(T--)
    {
        MAX=0,MIN=INF;
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;++i) pa[i]=read_point();
        for(int i=0;i<m;++i) pb[i]=read_point();
        ld LA=0,LB=0;
        for(int i=1;i<n;++i) LA+=length(pa[i]-pa[i-1]);
        for(int i=1;i<m;++i) LB+=length(pb[i]-pb[i-1]);
        int sa=0,sb=0;
        Point PA=pa[0],PB=pb[0];
        while(sa<n-1&&sb<m-1)
        {
            ld la=length(pa[sa+1]-PA);
            ld lb=length(pb[sb+1]-PB);
            ld T=min(la/LA,lb/LB);
            Vector va=(pa[sa+1]-PA)/la*T*LA;
            Vector vb=(pb[sb+1]-PB)/lb*T*LB;
            update(PA,PB,PB+vb-va);
            PA=PA+va;
            PB=PB+vb;
            if(PA==pa[sa+1]) ++sa;
            if(PB==pb[sb+1]) ++sb;
        }
        printf("Case %d: %d\n",kase++,(int)lround(MAX-MIN));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
项目:使用 JavaScript 编写的杀死幽灵游戏(附源代码) 杀死鬼魂游戏是使用 Vanilla JavaScript、CSS 和 HTML 画布开发的简单项目。这款游戏很有趣。玩家必须触摸/杀死游荡的鬼魂才能得分。您必须将鼠标悬停在鬼魂上 - 尽量得分。鬼魂在眨眼间不断从一个地方移动到另一个地方。您必须在 1 分钟内尽可能多地杀死鬼魂。 游戏制作 这个游戏项目只是用 HTML 画布、CSS 和 JavaScript 编写的。说到这个游戏的特点,用户必须触摸/杀死游荡的幽灵才能得分。游戏会根据你杀死的幽灵数量来记录你的总分。你必须将鼠标悬停在幽灵上——尽量得分。你必须在 1 分钟内尽可能多地杀死幽灵。游戏还会显示最高排名分数,如果你成功击败它,该分数会在游戏结束屏幕上更新。 该游戏包含大量的 javascript 以确保游戏正常运行。 如何运行该项目? 要运行此游戏,您不需要任何类型的本地服务器,但需要浏览器。我们建议您使用现代浏览器,如 Google Chrome 和 Mozilla Firefox。要玩游戏,首先,单击 index.html 文件在浏览器中打开游戏。 演示: 该项目为国外大神项目,可以作为毕业设计的项目,也可以作为大作业项目,不用担心代码重复,设计重复等,如果需要对项目进行修改,需要具备一定基础知识。 注意:如果装有360等杀毒软件,可能会出现误报的情况,源码本身并无病毒,使用源码时可以关闭360,或者添加信任。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值