Live2d Test Env

【几何】简单积累

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

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

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

 

向量是基础:

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

对于向量的点积:

               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)\n",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)\n",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\n",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\n",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!\n");
        else printf("No!\n");
    } 
    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\n",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\n",(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\n");
        else printf("NO\n");
    }return 0;
}
View Code

 

posted @ 2018-02-28 10:49  nimphy  阅读(302)  评论(0编辑  收藏  举报