SOJ1490 : Master ☆ Spark【极角排序、直线求交点、凸多边形求面积】

SOJ1490 : Master ☆ Spark

考点:极角排序、直线求交点、凸多边形求面积

解题思路

考场上的时候以为自己看懂了这个题目,以为很简单;但是我是没有学过计算几何的,我以为随便用用高中的数学知识就可以用模拟的方法写出来;越写越不对劲,这tm啥,一堆数据结构,考场上已经写了300多行了,自己写完的时候跑都跑不起来,虽然很明确自己需要得到什么,甚至答案都呼之欲出,就是很难写;总而言之,占用了大量机时不说,我还把自己会的让给了队友,结果都没写出回来,后面一个十分煎熬;

一开始想法就是去算截出来了多少面积,然后总面积减去;很好,接下来就可以求两部分,一部分是没射到的,一部分是射到了的;按照12年义务教育的经验,我毅然决然的选择了求没射到的面积,以为自己别具一格,出题人的秘密在这里;

然后,按照所谓的面向对象程序设计的理念,先写一堆struct类描述整个东西、点、线等等。从书上学来了求凸多边形的面积求法:说是需要一个内部的点进行,然后旋转计算面积,面积用叉乘解决;

这时差能写只有一步了,队友在过简单题,我注意到了关键词”不保证顺序“、还有我自己再写的时候知道顺时针逆时针这么旋转是很方便自己做题的,那个时候的问题变成给这个点进行一个”极坐标上“的大小排序了;考虑反三角函数,但是反三角函数不能顺利的生成 $[ 0,2 \pi ] $ 之间的所有角度,因此我自己用asin和acos拼凑了一个出来大概不错的东西;

接下来我只需要去割出那两个面积就好了,也就是求交点;但是自己线性代数没学好,让队友帮忙推了个线段和线段的交点,我比赛完了的那一周里面都是在用这个方法求交点的,简直害死我了。

但是只是去算没射到的,这样会有很多蛋痛的特殊情况:

  1. 交不到的时候,有的时候是两种,有的时候没有
  2. 也不是每个线段都能截出来面积的

我当时想到了一种就是太大了的情况,但是我连代码都跑不起来,就无从谈起这个方法那时候的七七八八了;

比赛结束后

我最想做出来的就是这个,因为我听完讲解之后感觉真不难,但是我还是花了不少时间去真正实现自己的代码,结果补题那几天一直猛WA,代码疯狂改版,心态崩了好几次;

其次就是自己做了一点点计算几何的题目,发现真不是自己想象的那样;什么凸包、偏序这些东西竟然很快就学到了,不是什么难东西,而自己写的模拟题却是十分多的细节错误,无言以对。

代码大改5、6次,全部重写2、3次。主要是真的不懂。

一些收获

  1. 极角排序,我一开始写的是按照重心排序的,一般的写法就是选一个最低点排序(最右点)进行排序的,不过那个是凸包里面的写法了;但是我那样写有没有错呢?
  2. 直线求交点:不是很清楚为什么没过,对拍输出的交点也是一点也不一样
  3. 可以发现这些东西本质上用点和向量去表示就完了,向量的本质就是点,所以全部用点,根本用不到边;
#include<bits/stdc++.h>

using namespace std;

const double PI=acos(-1);
inline int sgn(double number,double eps=1e-8){if(fabs(number)<eps)return 0;else return number>0?1:-1;}

struct Point {
    double x,y;
    Point(){;}
    Point(double vx,double vy):x(vx),y(vy){;}

    friend inline Point operator+(Point p1,Point p2){return Point(p1.x+p2.x,p1.y+p2.y);}
    friend inline Point operator-(Point p1,Point p2){return Point(p1.x-p2.x,p1.y-p2.y);}
    friend inline Point operator*(Point p,double factor){return Point(p.x*factor,p.y*factor);}
    friend inline Point operator*(double factor,Point p){return Point(p.x*factor,p.y*factor);}
    friend inline Point operator/(Point p1,double factor){return Point(p1.x/factor,p1.y/factor);}
    friend inline double operator*(Point p1,Point p2){return p1.x*p2.x+p1.y*p2.y;}
    friend inline double operator^(Point p1,Point p2){return p1.x*p2.y-p2.x*p1.y;}
    friend inline bool operator==(Point p1,Point p2){return !sgn(p1.x-p2.x) && !sgn(p1.y-p2.y);}
    friend inline bool operator!=(Point p1,Point p2){return sgn(p1.x-p2.x) || sgn(p1.y-p2.y);}

    void inline operator+=(Point p){x+=p.x,y+=p.y;}
    void inline operator-=(Point p){x-=p.x,y-=p.y;}
    void inline operator*=(double factor){x*=factor,y*=factor;}
    void inline operator/=(double factor){x/=factor,y/=factor;}
};

struct Line{
    Point p1,p2,Dir;
    double A,B,C;
    Line(){;}
    Line(Point vp1,Point vp2):p1(vp1),p2(vp2){if(p1.x>p2.x)swap(p1,p2);Dir=p2-p1;A=p2.y-p1.y,B=p1.x-p2.x,C=-(p1^p2);}
};

inline double Getlen(Point var){return sqrt(var*var);}
inline long long Getlen2(Point var){return var*var;}
inline double Getdis(Point p1,Point p2){return Getlen(p1-p2);}
inline double Getarea(Point p1,Point p2,Point p3){return fabs((p3-p1)^(p2-p1));}
inline Point Rotoate(Point p,double rad){return Point(p.x*cos(rad)-p.y*sin(rad),p.x*sin(rad)+p.y*cos(rad) );}
inline Point Normal(Point p){return Point(-p.y,p.x)/Getlen(p);}
inline double Getdis(Line l,Point p){return fabs(Normal(l.Dir)*(l.p1-p));}

inline bool Iscross(Point p1,Point p2,Point p3,Point p4){double s1=(p2-p1)^(p3-p1),s2=(p2-p1)^(p4-p1),s3=(p4-p3)^(p1-p3),s4=(p4-p3)^(p2-p3);return sgn(s1)*sgn(s2)<0 && sgn(s3)*sgn(s4)<0;}
inline bool Iscross(Point p1,Point p2,Line l){double s1=l.Dir^(p1-l.p1),s2=l.Dir^(p2-l.p1);return sgn(s1)*sgn(s2)<0;}
inline bool Iscross(Line l,Point p1,Point p2){double s1=l.Dir^(p1-l.p1),s2=l.Dir^(p2-l.p1);return sgn(s1)*sgn(s2)<0;}
inline bool Iscross(Line l1,Line l2){return sgn(l1.Dir^l2.Dir);}
//inline Point Getcross(Point p1,Point p2,Point p3,Point p4){double s1=(p2-p1)^(p3-p1),s2=(p2-p1)^(p4-p1);return Point(p3.x*s2-p4.x*s1,p3.y*s2-p4.y*s1)/(s2-s1);}
inline Line Parallel_move(Line l,double dis){Point temp=dis*Normal(l.Dir);return Line(l.p1+temp,l.p2+temp);}
inline Line Extend(Line l,double factor){factor=fabs(factor);return Line(l.p1-factor*l.Dir,l.p2+factor*l.Dir);}

const int N=120;

struct Node{
    int number;
    Point p[N];
    double Getarea(){
    	Point tmp=p[1];
    	double ans=0;
    	for(int i=2;i<number;i++){
    		ans+=(p[i]-tmp)^(p[i+1]-tmp)/2;
		}
		return ans;
	}
}tot;
int n;
Point Dir;
Point lpos;
Point rpos;

bool Getcross(const Point& a, const Point& b, const Point& p, Point& crs)
{
    if( sgn(Dir^(a-p))*sgn(Dir^(b-p))>0 ){
        return false;
    }else{
        if( !sgn(Dir^(a-p)) ){
            crs=a;
        }else if( !sgn(Dir^(b-p))  ){
            crs=b;
        }else{
            double angle1=acos(Dir*(a-p)/Getlen(Dir)/Getlen(a-p));
            if( sgn(angle1-PI/2)>0 )
                angle1=PI-angle1;
            double angle2=PI-angle1-acos((b-a)*(p-a)/Getlen(b-a)/Getlen(p-a));
            double len=Getlen(a-p)/sin(angle2)*sin(angle1);
            crs=a+(b-a)*len/Getlen(b-a);
        }
        return true;
    }
}



signed main(void)
{
    scanf("%d",&n);
    tot.number=n;
    for(int i=1;i<=n;i++){
        double x,y;
        scanf("%lf %lf",&tot.p[i].x,&tot.p[i].y);
    }

    sort(tot.p+2,tot.p+1+n,[&](Point p1,Point p2)->bool {
        int temp=sgn((p1-tot.p[1])^(p2-tot.p[1]));
        return temp ? (temp>0): (sgn(Getlen(p1-tot.p[1])-Getlen(p2-tot.p[1]))<0) ;
    });
    tot.p[tot.number+1]=tot.p[1];

    double w,px,py,fx,fy;
    scanf("%lf%lf%lf%lf%lf",&w,&px,&py,&fx,&fy);
    if(w<0)w=-w;
    Point pp(px,py);
    Point fp(fx,fy);
    Dir=fp-pp;
    lpos=pp+Normal(Dir)*(w/2);
    rpos=pp-Normal(Dir)*(w/2);


    auto check=[&]()->bool{
		bool ret1=1;
		for(int i=1; i<=n;i++){
			if( (Dir^(tot.p[i]-lpos)) < 0 ){
				ret1=0;
			}
		}
		bool ret2=1;
		for(int i=1; i<=n;i++){
			if( (Dir^(tot.p[i]-rpos)) > 0 ){
				ret2=0;
			}
		}
		return ret1||ret2;
	};
	if( check() ){
        puts("0.000000");
	}else{
	    Point tmp;
	    vector<Point > lsec,rsec,sec;
	    for(int i=1;i<=n;i++){
            if(Getcross(tot.p[i],tot.p[i+1],lpos,tmp)){
                lsec.emplace_back(tmp);
            }
	    }

	    for(int i=1;i<=n;i++){
            if(Getcross(tot.p[i],tot.p[i+1],rpos,tmp)){
                rsec.emplace_back(tmp);
            }
	    }

	    if( !lsec.size() && !rsec.size() ){
            puts("100.000000");return 0;
	    }else if(!lsec.size()){
            sec=rsec;
            for(int i=1;i<=n;i++){
                if( sgn(Dir^(tot.p[i]-rpos)) >= 0 ){
                    sec.emplace_back(tot.p[i]);
                }
            }
	    }else if(!rsec.size()){
            sec=lsec;
            for(int i=1;i<=n;i++){
                if( sgn( Dir^(tot.p[i]-lpos) ) <= 0 ){
                    sec.emplace_back(tot.p[i]);
                }
            }
	    }else{
	        sec=lsec;
	        for(auto &it:rsec)
                sec.emplace_back(it);
            for(int i=1;i<=n;i++){
                if( sgn(Dir^(tot.p[i]-rpos)) >= 0 && sgn(Dir^(tot.p[i]-lpos)) <= 0 ){
                    sec.emplace_back(tot.p[i]);
                }
            }
	    }

	    tmp=sec[0];
        sort(sec.begin(),sec.end(),[&](Point p1,Point p2)->bool {
            int temp=sgn((p1-tmp)^(p2-tmp));
            return temp ? (temp>0): (sgn(Getlen(p1-tmp)-Getlen(p2-tmp))<0) ;
        });

        double ans=0;
        for(int i=2;i<sec.size();i++){
            ans+=((sec[i-1]-sec[0])^(sec[i]-sec[0]))/2;
        }
        printf("%.6lf\n",ans*100/tot.Getarea());
	}

    return 0;
}

总结一下致命错误:

  1. 首先比赛是注重效率,以最顺手,最快的方式或者是代码复用率最高的方式去做题,AC才是真神
  2. 计算几何不是初高中生做的什么数学题,而让计算机去算的;计算机怎么写最好,怎么写最顺手,这是我要学的,不是抱残守缺;
  3. 数学课真不能在划水了
posted @ 2023-05-04 18:02  ZZQ323  阅读(26)  评论(0)    收藏  举报