• ZZNU-OJ-2118 -(台球桌面碰来碰去,求总距离)——模拟到爆炸【超时】的不能AC的代码


    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 1
    1 1 1 1 2 1

    样例输出

    复制
    1.41
    2.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 #include <iostream>
      2 #include<stdio.h>
      3 #include<string.h>
      4 #include<string>
      5 #include<algorithm>
      6 #define ll long long
      7 using namespace std;
      8 #include<math.h>
      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)<eps)&&(fabs(a.y-b.y)<eps);
     34 }
     35 double cross(Vector t,Vector f) //计算两个向量的叉乘 之模
     36 {
     37     return (t.x*f.y-t.y*f.x);
     38 }
     39 double cross1(Point  e, Point  f,  Point  g)///向量ef与向量eg的叉积,左边为负值, 锐角为正值
     40 {
     41     return cross(f-e,g-e);
     42 }
     43 
     44 int factcover(  Point  e,  Point  f,  Point  g,  Point  h)///判断两个线段是否相交, 如果不包括端点 直接<0 包括端点为<=minn(极小值)
     45 {
     46     if(min(e.x,f.x)<=max(g.x,h.x)&&///要避免某一边的端点值  在另一边的延长线上
     47             max(e.x,f.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)<eps)
     99         {
    100             flag=i;break;
    101         }
    102     }
    103 
    104     if(flag==0)   //本次弹射将撞到 第flag的边上
    105     {
    106         for(int i=1;i<=4;i++){  ///先平判断撞到编号为 flAG的 边上
    107             if(i!=begnum&&factcover(s.e,s.f,L[i].e,L[i].f)==1){
    108                 flag=i;
    109                 break;
    110             }
    111          }
    112 
    113 
    114         double newk,newb;
    115 
    116    // cout<<" 边:"<<flag;
    117         if(flag==1||flag==3)  //两条竖着的边
    118         {
    119             double x=L[flag].e.x;
    120             double y=s.k*x+s.b;
    121             ans+=Length(p0-Point(x,y));
    122             p0=Point(x,y);
    123 
    124             newk=-1*s.k;
    125             newb=y-x*newk;
    126         }
    127         else  //两条横着的矩形边
    128         {
    129             double y=L[flag].e.y;
    130             double x=(y-s.b)/s.k;
    131             ans+=Length(p0-Point(x,y));
    132             p0=Point(x,y);
    133 
    134             newk=-1*s.k;
    135             newb=y-x*newk;
    136         }
    137 
    138         s.k=newk;
    139         s.b=newb;///更新反射后的射线S 的k和b
    140     }
    141     else
    142     {
    143 
    144  // cout<<" 角:"<<flag;
    145         ans+=Length(p0-p[flag]);
    146         p0=p[flag];
    147 
    148         s.k=s.k;
    149         s.b=s.b;///碰撞到四个角后原路返回,k不变!b不变!
    150     }
    151 
    152 }
    153 int main()
    154 {
    155 
    156     while(scanf("%lf",&X)!=EOF)
    157     {
    158         scanf("%lf%lf%lf%lf%lf",&Y,&a,&b,&E,&W);
    159         init(X,Y);
    160         int Time=0;
    161 
    162         Point p0=Point(0,0);  //初始起点和射线
    163         line s;
    164         s.k=b / a;
    165         s.b=0.0;
    166         double ans=0.0;
    167 
    168         while(E>0)
    169         {
    170            ++Time;
    171           //  if(Time%100000==0)printf(" Time*1e^5=%d
    ",Time);
    172             int begnum=-1;
    173             for(int i=4; i>=1; i--) //四个角编号为1+4--4+4
    174             {
    175                 if(p[i]==p0){
    176                      begnum=i+4;break;
    177                 }
    178 
    179             }
    180             for(int i=1; begnum==-1&&i<=4; i++) //四条边为1--4
    181             {
    182                 if(DisToLine(p0,L[i].e,L[i].f)<eps)
    183                 {
    184                     begnum=i;
    185                 }
    186             }
    187 
    188          //   printf("beg=%d p0= %lf,%lf  s.k=%lf s.b=%lf total_ans:%lf  ",begnum,p0.x,p0.y,s.k,s.b,ans);
    189             if(Time>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,b
    196             E-=W;//减去动能
    197         }
    198         printf("%.2lf
    ",ans);
    199     }
    200 
    201     return 0;
    202 }
    View Code(点一下写一天,bug不花一分钱)
  • 相关阅读:
    CodeSmith注册错误的解决方法
    我是“坚守者”还是"背叛者"?
    拿什么留住你,我的程序员
    去除HTML代码得函数
    页面之间传递参数得几种方法
    nhibernate source code analyzed (abstract classes in nhibernate2.0)
    Web 2.0时代RSS的.Net实现
    Visual Studio.net 2003安装提示重启问题
    开放思路,综合考虑,心胸开阔,做一个合格的项目经理
    了解实际开发中 Hashtable 的特性原理 .NET, JAVA, PHP
  • 原文地址:https://www.cnblogs.com/zhazhaacmer/p/9378963.html
Copyright © 2020-2023  润新知