ZZNU-2118 : 早安晚安,不如我先入土为安
题目描述
spring比较喜欢玩台球,因为看着台球在桌子上碰来碰去很有意思(台球撞壁反弹,入射角等于反射角),每次完美的台球入洞,都能体现他数学天才的能力。机房的大佬们当然不承认spring能力强,而是认为每次都是运气而已。
spring很不服气,但又打不过机房大佬,争执过程中聪明的渣渣宥终于想到了完美的办法,那也就是建立数学模型,交给脸红脖子粗的spring来解决。
题目给出一组(x,y),表示矩形的四个点分别为(0,0)(x,0)(0,y)(x,y),构成一个密闭的矩形,只有一个入口(也是唯一的出口)在原点也就是(0,0),假设一个点在原点按照角度(ay=bx)射入矩形中,所拥有的动能为E,每次接触墙壁并反弹所消耗的动能为W,如果射到除原点以外的三个顶点,将原路返回,并且消耗动能W。忽略摩擦力的影响,求出球在矩阵中运动的位移之和,保留两位有效数字。
输入
输入为六个正整数x,y,a,b,E,W。x,y,a,b均小于2000,E,W属于int
输出
求出球在矩阵中运动的位移之和,保留两位有效数字。
样例输入
复制
1 1 1 1 1 11 1 1 1 2 1
样例输出
复制
1.412.83
模拟了四个小时,WA了,瞬间原地爆炸了;
第二天接着找bug,调了后台数据看了看发现一些细节问题,做了一天的这道题,完整地复习了好多几何的知识点!!
我把超时的代码放着了,纪念一下下。
思路是对的,针对上限E=2*10^9,模拟——时间显然会超限!23333
放一组后台数据,可能会对你有所帮助——
INPUT
1 3 3 1 17 1
OUTPUT
17.92
超时的代码,大部分数据正确——
思路:
1、每一步严格分类讨论,计算出是否撞到源点(第一次例外),是否撞到其他三个顶点——具体是哪一个顶点,是否撞到四条边——具体是那一条边!然后分别根据具体情况,求出相应碰撞时的坐标和反射角等参数,进入下一次循环!
2、初始时,由一个顶点和一个ay=bx的射线方程可以推出这条射线的方程的K和b ,简单的方法就是把它延伸成一条相当长的线段来处理,只需坐标加上M和K*M即可!
3、代码精简了好几次,发现某些思路写的代码其实可以大大省去!
4、还有其他方法也阔以解决,比如:数学方法,相似比方法。
1 #include2 #include 3 #include 4 #include 5 #include 6 #define ll long long 7 using namespace std; 8 #include 9 #define N 1008 10 #define M 10008 11 #define dinf 1000000.0 12 double X,Y,a,b,E,W; 13 const double eps=1e-8; 14 #define PI 3.14159265358 15 struct Point 16 { 17 double x,y; 18 Point(double x=0.0,double y=0.0):x(x),y(y) {} 19 } p[5]; 20 typedef Point Vector; 21 struct line 22 { 23 Point e,f; 24 double k,b; 25 } L0,L[5]; 26 27 Vector operator-(Point a,Point b) 28 { 29 return Vector(a.x-b.x,a.y-b.y); 30 } 31 bool operator==(Point a,Point b) 32 { 33 return (fabs(a.x-b.x) =min(g.x,h.x)&& 48 min(e.y,f.y)<=max(g.y,h.y)&& 49 max(e.y,f.y)>=min(g.y,h.y)&& ///<=也可以;前四行 大大于小, 大大于小, 大大于小, 大大于小 50 cross1(e,f,g)*cross1(e,f,h)<0&& ///两条边相交 端点判断容易出错 便需要两条边同时进行; ///己方向量加一异点, 此行相交时结果为负值或0 51 cross1(e,g,h)*cross1(f,g,h)<0) ///如果两者共点 返回值也是1///轮流己方一点指向两个异点, 此行相交时结果为负值或0 52 return 1; ///0 0 1 1 3 3 4 4 返回0 0 0 9 9 3 3 4 4 返回1 53 return 0; ///0 0 3 3 3 3 4 4 返回1 0 0 3 3 3 3 4 5 返回1 54 } 55 56 void init(double x,double y) //四个顶点,四条边,初始点p0 57 { 58 p[1]=Point(0,0); 59 p[2]=Point(0,y); 60 p[3]=Point(x,y); 61 p[4]=Point(x,0); 62 63 L[1].e=p[1],L[1].f=p[2]; 64 L[1].k=dinf; 65 L[2].e=p[2],L[2].f=p[3]; 66 L[2].k=0; 67 L[3].e=p[3],L[3].f=p[4]; 68 L[3].k=dinf; 69 L[4].e=p[4],L[4].f=p[1]; 70 L[4].k=0; 71 72 } 73 74 double Length(Vector A) //利用点积求向量长度 75 { 76 return sqrt(A.x*A.x+A.y*A.y); 77 } 78 79 double DisToLine(Point P,Point A,Point B) //点到直线的距离 80 { 81 Vector v1=B-A,v2=P-A; 82 return fabs(cross(v1,v2))/Length(v1); 83 } 84 void factk(line &s) //将线段的边长延伸M倍 85 { 86 s.e.x+=1.0*M; 87 s.e.y+= s.k * M; 88 s.f.x-=1.0*M; 89 s.f.y-= s.k * M; 90 } 91 void launch(line &s,Point &p0,int begnum, double &ans) //反射,并返回碰撞数据 92 { 93 //求触碰边(角落),返回 反射后的射线S的 p0、k、b系数; 94 int flag=0; 95 //先检查是否撞到了四个点上,再检查是否撞到了四条边上 96 for(int i=1; i<=4; i++) ///直接拿延长后的射线同四个顶点进行求最短距离,为0的为碰撞角 97 { 98 if(i!=begnum-4 && DisToLine(p[i],s.e,s.f) 0)169 {170 ++Time;171 // if(Time%100000==0)printf(" Time*1e^5=%d\n",Time);172 int begnum=-1;173 for(int i=4; i>=1; i--) //四个角编号为1+4--4+4174 {175 if(p[i]==p0){176 begnum=i+4;break;177 }178 179 }180 for(int i=1; begnum==-1&&i<=4; i++) //四条边为1--4181 {182 if(DisToLine(p0,L[i].e,L[i].f) 1&&begnum==5)break;///从源点冲出去了190 191 s.e=p0;192 s.f=p0;//当前射线方程;193 factk(s);//由点扩展成线194 195 launch(s,p0,begnum,ans);//发射小球,计算出新的碰撞点存入p0中,并更改a,b196 E-=W;//减去动能197 }198 printf("%.2lf\n",ans);199 }200 201 return 0;202 }