zoukankan      html  css  js  c++  java
  • 【几何】简单积累

    由于几何问题一般是压轴题,对我来说过于复杂,而且这一块是交给队友了的,所以自己都没怎么做过。

    为了应对蓝桥杯,和一些简单比赛,还是应该做几个几何水题,以免到时候遇到很水的题我都没有信心去做。

    所以,下面的东西都很水,大神就不要浪费时间看了。 

    向量是基础:

                  可以方便使用加减乘除,可以有很多模板,而且能尽可能避免除法和三角函数,精度高,效率高。

    对于向量的点积:

                   p*q*cos<o>  求同向还是异向;求投影;求出投影后用勾股定理求点到直线距离。

    对于向量的叉积:

                   p*q*sin<o>   求面积;求顺时针方向还是逆时针方向;判断是否在半平面上。

    (叉积表示有向面积,小于180度为正。)

    POJ2007:Scrambled Polygon 

    题意:给定凸多边形的顶点(保证第一个是(0,0)),求按顺序输出顶点(保证无三点共线)。

    思路:利用叉积排序,两个向量(顶点减(0,0))的叉积a^b,如果a^b>0,说明b在a的顺时针方向。(如果不是凸多边形不成立,如果三点共线不成立)。

    //对于凸多边形,以0为起点的向量,叉积排序。 struct Vector { int x;int y;} node[maxn];
    bool cmp(Vector a,Vector b)
    {
        return (a.x*b.y-a.y*b.x)>0;
    }
    sort(node+1,node+cnt,cmp);
    for(int i=0;i<cnt;i++) printf("(%d,%d)
    ",node[i].x,node[i].y);
     
    //对于凸多边形,以0为起点的向量,叉积排序。 
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=1000000;
    struct Vector 
    {
        int x;int y;
        bool friend operator < (Vector a,Vector b)
        {
            return a.x*b.y>b.x*a.y;
        }
    };
    Vector node[maxn];
    int main()
    {
        int cnt=0;
        while(~scanf("%d%d",&node[cnt].x,&node[cnt].y)) cnt++;
        sort(node+1,node+cnt);
        for(int i=0;i<cnt;i++) printf("(%d,%d)
    ",node[i].x,node[i].y);
        return 0;
    }
    View Code

    HDU2036:改革春风吹满地 :

    题意:按顺序给出多边形(有可能是凸多边形,有可能不是),求其面积。

    思路:划分为若干个三角形,以第一个点为每个三角形的一个顶点避免重复,叉乘的1/2是面积,不加绝对值,避免出现凹多边形是面积算大了。

    Vector friend operator -(Vector a,Vector b) {
            return Vector(a.x-b.x,a.y-b.y);
    }
    double friend operator ^(Vector w,Vector v) {//叉积 
            return w.x*v.y-v.x*w.y;
    }
            cin>>a.x>>a.y>>b.x>>b.y;
            for(i=3;i<=n;i++){
                cin>>c.x>>c.y; 
                ans+=0.5*((b-a)^(c-a));
                b.x=c.x; b.y=c.y;
            }
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<memory.h>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    struct Vector
    {
        int x; int y;
        Vector(){}
        Vector(int xx,int yy):x(xx),y(yy){}
        Vector friend operator -(Vector a,Vector b) {
            return Vector(a.x-b.x,a.y-b.y);
        }
        double friend operator ^(Vector w,Vector v) {//叉积 
            return w.x*v.y-v.x*w.y;
        }
    };
    Vector a,b,c;
    int main()
    {
        int i,n;
        double ans,temp;
        while(~scanf("%d",&n)){
            if(n==0) return 0; ans=0; 
            cin>>a.x>>a.y>>b.x>>b.y;
            for(i=3;i<=n;i++){
                cin>>c.x>>c.y; 
                ans+=0.5*((b-a)^(c-a));
                b.x=c.x; b.y=c.y;
            }
            printf("%.1lf
    ",ans);
        }
        return 0;
    }
    View Code

    POJ1569:Myacm Triangles

     题意:很少的点,求三个点,组成的三角形面积最大,且其他点不在三角形边上或者内部。

    思路:枚举点,然后检查其他点是否在内部或者边上。我知道的检查方式有两种(可能还有很多种):

    1,经过点画一条直线,与多边形的交点个数为偶则在内部。

    2,叉积判断,对于凸多边形,用逆时针表示向量方向,如果一个点在所有边的左边,则它在多边形内部。

    这里用的第二种,逆时针表示可以通过叉积排序做到(见上面第一题)。

    int Vector_S(int a,int b,int c)
    {
        int tmp=(node[b].x-node[a].x)*(node[c].y-node[a].y)-(node[c].x-node[a].x)*(node[b].y-node[a].y);//叉积 
        if(tmp==0) return -1;//三点共线,不构成三角形 
        else if(tmp<0) swap(b,c),tmp=-tmp;//叉积排序,保证是逆时针的三角形。 
        for(int i=1;i<=N;i++){
            if(i==a||i==b||i==c) continue;
            bool Flag=true; //如果有一个在右边则不是在多边形内部。 
            if((node[b].x-node[a].x)*(node[i].y-node[a].y)<(node[i].x-node[a].x)*(node[b].y-node[a].y)) Flag=false;
            if((node[c].x-node[b].x)*(node[i].y-node[b].y)<(node[i].x-node[b].x)*(node[c].y-node[b].y)) Flag=false; 
            if((node[a].x-node[c].x)*(node[i].y-node[c].y)<(node[i].x-node[c].x)*(node[a].y-node[c].y)) Flag=false;
            if(Flag) return -1;
        }
        return tmp;
    }
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    struct Point{
        char id[2];
        int x,y;
        Point(){}
    }node[20]; int N;
    int Vector_S(int a,int b,int c)
    {
        int tmp=(node[b].x-node[a].x)*(node[c].y-node[a].y)-(node[c].x-node[a].x)*(node[b].y-node[a].y);
        
        if(tmp==0) return -1;
        else if(tmp<0) swap(b,c),tmp=-tmp;//保证是逆时针的三角形。 
        for(int i=1;i<=N;i++){
            if(i==a||i==b||i==c) continue;
            bool Flag=true; //如果有一个在右边则不是在多边形内部。 
            if((node[b].x-node[a].x)*(node[i].y-node[a].y)<(node[i].x-node[a].x)*(node[b].y-node[a].y)) Flag=false;
            if((node[c].x-node[b].x)*(node[i].y-node[b].y)<(node[i].x-node[b].x)*(node[c].y-node[b].y)) Flag=false; 
            if((node[a].x-node[c].x)*(node[i].y-node[c].y)<(node[i].x-node[c].x)*(node[a].y-node[c].y)) Flag=false;
            if(Flag) return -1;
        }
        return tmp;
    }
    int main()
    {
        int i,j,k,a,b,c,Max;
        while(~scanf("%d",&N)){
            Max=-1;
            if(N==0) break;
            for(i=1;i<=N;i++) scanf("%s%d%d",node[i].id,&node[i].x,&node[i].y);
            for(i=1;i<=N;i++)
             for(j=1;j<i;j++)
              for(k=1;k<j;k++){
                  int tmp=Vector_S(i,j,k);
                  if(tmp>Max) Max=tmp,a=i,b=j,c=k;
            }
            if(node[a].id[0]>node[b].id[0]) swap(a,b);
            if(node[b].id[0]>node[c].id[0]) swap(b,c);
            if(node[a].id[0]>node[b].id[0]) swap(a,b);
            printf("%c%c%c
    ",node[a].id[0],node[b].id[0],node[c].id[0]);
        }
        return 0;
    }
    View Code

     

    POJ3304:Segments

    题意:已知N条线段,问是否存在直线X,使得这些线段在直线上的投影有公共点。

    思路:转化一下,就是问是是否存在直线X,使得X的垂线L与每个线段都有公共点。如果存在L,那么把其中一些满足条件的L平移,

    可以使得它结果两个已知线段的端点。

    注意一下:需要判断是否两点重合,开始我用的方法是fabs(x1-x2)<eps,fabs(y1-y2)<eps则重合,改为dist<=eps则重合就AC了。

    bool check(Cpoint w, Cpoint v)
    {
        if(dist(w,v)<=eps) return false; //判断两点重合,开始用点直接比较WA了。 
        Vector base(w.x-v.x,w.y-v.y);
        for(int i=1;i<=N;i++){
            Vector e(L[i].a.x-v.x,L[i].a.y-v.y);
            Vector p(L[i].b.x-v.x,L[i].b.y-v.y);
            if((base^e)*(base^p)>eps) return false; 
        } return true;
    }
    bool find()
    {
        for(int i=1;i<=N;i++)
          for(int j=1;j<=N;j++){
             if(check(L[i].a,L[j].a)) return true;
             if(check(L[i].a,L[j].b)) return true;
             if(check(L[i].b,L[j].a)) return true;
             if(check(L[i].b,L[j].b)) return true;
        }
        return false;
    }
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=110;
    const double eps=1e-8;
    int N;
    struct Cpoint
    {
        double x,y;
        Cpoint(){}
        Cpoint(double xx,double yy):x(xx),y(yy){}
    };
    struct Cline
    {
        Cpoint a,b;
        Cline(){}
        Cline(Cpoint aa,Cpoint bb):a(aa),b(bb){}
    };
    struct Vector
    {
        double x,y;
        Vector(){}
        Vector(double xx,double yy):x(xx),y(yy){}
        double friend operator ^(Vector a,Vector b){
            return a.x*b.y-b.x*a.y;
        }
    };
    double dist(Cpoint a,Cpoint b)
    {
        return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
    Cline L[maxn];
    bool check(Cpoint w, Cpoint v)
    {
        if(dist(w,v)<=eps) return false;// 判断两点重合,开始用点直接比较WA了。 
        Vector base(w.x-v.x,w.y-v.y);
        for(int i=1;i<=N;i++){
            Vector e(L[i].a.x-v.x,L[i].a.y-v.y);
            Vector p(L[i].b.x-v.x,L[i].b.y-v.y);
            if((base^e)*(base^p)>eps) return false; 
        } return true;
    }
    bool find()
    {
        for(int i=1;i<=N;i++)
          for(int j=1;j<=N;j++){
             if(check(L[i].a,L[j].a)) return true;
             if(check(L[i].a,L[j].b)) return true;
             if(check(L[i].b,L[j].a)) return true;
             if(check(L[i].b,L[j].b)) return true;
        }
        return false;
    }
    int main()
    {
        int T; double x,y,xx,yy;  
        scanf("%d",&T);
        while(T--){
            scanf("%d",&N);
            for(int i=1;i<=N;i++){
                scanf("%lf%lf%lf%lf",&x,&y,&xx,&yy);
                L[i]=Cline(Cpoint(x,y),Cpoint(xx,yy));
            }
            if(find()) printf("Yes!
    ");
            else printf("No!
    ");
        } 
        return 0;
    }
    View Code

    POJ1066:Treasure Hunt 

    题意:一些线段把100*100的正方形分割成一些多边形,现在要求从正方形边界到达某一点P,每次只能穿过多边形的边的中点,求至少穿过多少边。

    思路:开始我一位每次只能穿过每条线段的中点,那么这样只有最多N个点,可以用最短路算法求。 但是是多边形的中点,这个限制可以忽略,所以直接求交点就行。

    枚举起点S,连接P,求交点个数,但是需要完全相交,即在边缘的不算,即起点S也是不算的,表示我们枚举是起点S,其实代表的是S附近的一段线段,所以我们枚举正方形上的点,其实代表了100*100的正方形。

    注意:精度,线段完全相交我们使用叉积判断。

        Cpoint P(x,y);
        for(int i=1;i<=N;i++){ //枚举线段 
            Cpoint tmp1=L[i].a;
            ans=min(ans,find_intersect(Cline(P,tmp1))); 
            Cpoint tmp2=L[i].b;
            ans=min(ans,find_intersect(Cline(P,tmp2)));
        }
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=100;
    const double eps=1e-8;
    int N,ans;
    struct Cpoint
    {
        double x,y;
        Cpoint(){}
        Cpoint(double xx,double yy):x(xx),y(yy){}
    };
    struct Cvector 
    {
        double x,y;
        Cvector(){}
        Cvector(double xx,double yy):x(xx),y(yy){}
        double friend operator ^(Cvector w,Cvector v)//线段完全相交 
        {
            return w.x*v.y-v.x*w.y;
        }
    };
    struct Cline 
    {
        Cpoint a,b;
        Cline(){}
        Cline(Cpoint aa,Cpoint bb):a(aa),b(bb){}
        bool friend operator *(Cline w,Cline v)//线段完全相交 
        {
            Cvector x1(w.b.x-w.a.x,w.b.y-w.a.y);
            Cvector x2(v.b.x-w.a.x,v.b.y-w.a.y);
            Cvector x3(v.a.x-w.a.x,v.a.y-w.a.y);
            if((x1^x2)*(x1^x3)>=-eps) return false;
            x1=Cvector(v.b.x-v.a.x,v.b.y-v.a.y);
            x2=Cvector(w.b.x-v.a.x,w.b.y-v.a.y);
            x3=Cvector(w.a.x-v.a.x,w.a.y-v.a.y);
            if((x1^x2)*(x1^x3)>=-eps) return false;
            return true;
        }
    };
    Cline L[maxn];
    int find_intersect(Cline x)
    {
        int res=0;
        for(int i=1;i<=N;i++){
            if(x*L[i]) res++;
        } return res;
    }
    int main()
    {
        double x,y,z,w;
        scanf("%d",&N); ans=N;
        for(int i=1;i<=N;i++){
            scanf("%lf%lf%lf%lf",&x,&y,&z,&w);
            L[i]=Cline(Cpoint(x,y),Cpoint(z,w));
        }
        scanf("%lf%lf",&x,&y);
        Cpoint P(x,y);
        for(int i=1;i<=N;i++){ //枚举线段 
            Cpoint tmp1=L[i].a;
            ans=min(ans,find_intersect(Cline(P,tmp1))); 
            Cpoint tmp2=L[i].b;
            ans=min(ans,find_intersect(Cline(P,tmp2)));
        }
        printf("Number of doors = %d
    ",ans+1);
        return 0;
    }
    View Code

    POJ1113: Wall

    题意:给定N个点,求用一个多边形把这些点包括进去,且每个点到多边形的距离都大于等于L。

    思路:

            先不考虑L这个条件,因为两点之间,直线最短,所以对于凹进去的部分,我们肯定有最短的直线可以包含它,可以忽略,所以是求凸包。

            然后考虑L,对于求出的凸多边形,对于它的顶点X,可以证明每个X附近需要增加一定的圆弧来保证顶点到圆弧的距离大于等于L,

            所有X的圆弧角度之和为Pi,所以ans=凸包+2*Pi*L。

        sort(P+1,P+N+1); //得到“原点 ” 
        sort(P+2,P+N+1,cmp);  //得到积角序 
        int q[maxn],top=3;
        q[1]=1; q[2]=2; q[3]=3;
        for(int i=4;i<=N;i++){
            while(top>1&&Sign((P[q[top]]-P[q[top-1]])^(P[i]-P[q[top]]))<=0) top--;
            q[++top]=i;
        }
        for(int i=1;i<top;i++) res+=dist(P[q[i]],P[q[i+1]]);
        res=res+dist(P[q[top]],P[1])+2.0*pi*L;
        return res;
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=1010;
    const double pi=acos(-1.0);
    const double eps=1e-6;
    struct Cpoint
    {
        double x,y;
        Cpoint(){}
        Cpoint(double xx,double yy):x(xx),y(yy){}
        Cpoint friend operator -(Cpoint a,Cpoint b){
            return Cpoint(a.x-b.x, a.y-b.y);
        }
        double friend operator ^(Cpoint a,Cpoint b){
            return a.x*b.y-b.x*a.y;
        }
        bool friend operator <(Cpoint a,Cpoint b){
            if(a.y==b.y) return a.x<b.x;
            return a.y<b.y;
        }
    };
    double dist(Cpoint a,Cpoint b)
    {
        return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
    int Sign(double x)
    {
        if(x>=-eps&&x<=eps) return 0;
        if(x>eps) return 1; return -1;
    }
    int N,L; Cpoint P[maxn];
    bool cmp(Cpoint a,Cpoint b)
    {
        int s=Sign((a-P[1])^(b-P[1]));
        if(s>0||(s==0&&dist(a,P[1])<dist(b,P[1]))) return true;
        return false;
    }
    double Graham()  //如果N<3还得讨论一下。 
    {
        double res=0;
        sort(P+1,P+N+1); //得到“原点 ” 
        sort(P+2,P+N+1,cmp);  //得到积角序 
        int q[maxn],top=3;
        q[1]=1; q[2]=2; q[3]=3;
        for(int i=4;i<=N;i++){
            while(top>1&&Sign((P[q[top]]-P[q[top-1]])^(P[i]-P[q[top]]))<=0) top--;
            q[++top]=i;
        }
        for(int i=1;i<top;i++) res+=dist(P[q[i]],P[q[i+1]]);
        res=res+dist(P[q[top]],P[1])+2.0*pi*L;
        return res;
    }
    int main()
    {
        while(~scanf("%d%d",&N,&L)){
            for(int i=1;i<=N;i++)
                scanf("%lf%lf",&P[i].x,&P[i].y);
            printf("%d
    ",(int)(Graham()+0.5));
        }return 0;
    }
    View Code

    POJ1228:Grandpa's Estate 

    题意:给定一些点,问是否能确定一个唯一的凸包。

    思路:求出凸包后,每条边上若至少有3个点,则可以确定。

        if(N<6) return false;//肯定不构成凸包 
        sort(P+1,P+N+1); //得到“原点 ” 
        sort(P+2,P+N+1,cmp);  //得到积角序 
        int q[maxn],top=2;
        q[1]=1; q[2]=2; //q[3]=3;
        for(int i=3;i<=N;i++){
            while(top>1&&Sign((P[q[top]]-P[q[top-1]])^(P[i]-P[q[top]]))<=0) top--;
            q[++top]=i;
        }
        if(top<3) return false; //不构成凸包 
        for(int i=1;i<top;i++) if(find(P[q[i]],P[q[i+1]])<3) return false;
        if(find(P[q[top]],P[q[1]])<3) return false;
        return true;
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int maxn=1010;
    const double pi=acos(-1.0);
    const double eps=1e-6;
    struct Cpoint
    {
        double x,y;
        Cpoint(){}
        Cpoint(double xx,double yy):x(xx),y(yy){}
        Cpoint friend operator -(Cpoint a,Cpoint b){
            return Cpoint(a.x-b.x, a.y-b.y);
        }
        double friend operator ^(Cpoint a,Cpoint b){
            return a.x*b.y-b.x*a.y;
        }
        bool friend operator <(Cpoint a,Cpoint b){
            if(a.y==b.y) return a.x<b.x;
            return a.y<b.y;
        }
    };
    double dist(Cpoint a,Cpoint b)
    {
        return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
    }
    int Sign(double x)
    {
        if(x>=-eps&&x<=eps) return 0;
        if(x>eps) return 1; return -1;
    }
    int N; Cpoint P[maxn];
    bool cmp(Cpoint a,Cpoint b)
    {
        int s=Sign((a-P[1])^(b-P[1]));
        if(s>0||(s==0&&dist(a,P[1])<dist(b,P[1]))) return true;
        return false;
    }
    int find(Cpoint a,Cpoint b)
    {
        int res=0;
        for(int i=1;i<=N;i++){
            if(Sign((P[i].x-a.x)*(P[i].x-b.x))>0) continue;
            if(Sign((P[i].y-a.y)*(P[i].y-b.y))>0) continue;
            if(Sign((b-a)^(P[i]-a))==0) res++;
        }
        return res;
    }
    bool Graham()
    {
        if(N<6) return false;//肯定不构成凸包 
        sort(P+1,P+N+1); //得到“原点 ” 
        sort(P+2,P+N+1,cmp);  //得到积角序 
        int q[maxn],top=2;
        q[1]=1; q[2]=2; //q[3]=3;
        for(int i=3;i<=N;i++){
            while(top>1&&Sign((P[q[top]]-P[q[top-1]])^(P[i]-P[q[top]]))<=0) top--;
            q[++top]=i;
        }
        if(top<3) return false; //不构成凸包 
        for(int i=1;i<top;i++) if(find(P[q[i]],P[q[i+1]])<3) return false;
        if(find(P[q[top]],P[q[1]])<3) return false;
        return true;
    }
    int main()
    {
        int T;scanf("%d",&T);
        while(T--){
            scanf("%d",&N);
            for(int i=1;i<=N;i++)
                scanf("%lf%lf",&P[i].x,&P[i].y);
            if(Graham()) printf("YES
    ");
            else printf("NO
    ");
        }return 0;
    }
    View Code
  • 相关阅读:
    原生Python机器学习分类之一Knn算法
    Java可视化文件(夹)加密解密压缩解压
    基于图搜索技术的八数码问题求解C++
    遗传算法解决TSP问题
    简单dp
    并查集
    KMP算法
    快速迭代
    为什么vs2017在代码右键上没有vs2013(第一个图)上实现抽象类这个选项?
    关于C#面向对象中的查看类图(没有此按键的问题)的解决方法 The solution to view class diagrams in C # object-oriented (without this key)
  • 原文地址:https://www.cnblogs.com/hua-dong/p/8482389.html
Copyright © 2011-2022 走看看