原题:在平面直角坐标系下,台球桌是一个左下角在(0,0),右上角在(L,W)的矩形。有一个球心在(x,y),半径为R的圆形母球放在台球桌上(整个球都在台球桌内)。受撞击后,球沿极角为a的射线(即:x正半轴逆时针旋转到此射线的角度为a)飞出,每次碰到球桌时均发生完全弹性碰撞(球的速率不变,反射角等于入射角)。
如果球的速率为v,s个时间单位之后球心在什么地方?
分析:我们可以把运动正交分解成x与y两个方向上面的运动,这样我们就能分别处理两个方向上面的运动,尽可能地减少运算量。
方法1:(离散化)在时间没有足以让小球碰壁的部分,只需要让两个坐标自己不断加上单位位移,且设置时间量自增使其不超过设定时间即可;
当时间超出了设定时间时,我们不难发现,超过边界的线段部分与实际碰到边界后的对应运动部分线段是以1边界为对称轴对称的,因此,这部分只要做好判断条件,并且对速度方向以及运动后的左边(利用与假设运动后的坐标关系)做出修正即可。这样,就能循环出来最终的坐标。这边作者较懒,修正部分的公式还请大家自行推导或者说根据程序自己推算。以下是演示代码:
c++语言版本的:
#include
using namespace std;
int main()
{ int root,left,right,under;//提前设置好四个边界名字
double L,W,R,x,y,a,v,s;double k=acos(-1);double vx,vy;int t;
while(1){
cin>>L>>W>>x>>y>>R>>a>>v>>s;
if(L==0)break; //设置好跳出条件
vx=cos(a*k/180)*v;
vy=sin(a*k/180)*v;
t=0;//时间计算量要在每个 循环内及时归零。
left=R;
right=L-R;
root=W-R;
under=R;
while(t<s){//计算时间,离散化
x+=vx;
y+=vy;
while( (x
{
if(x<left)x=2*R-x,vx=-vx;
if(x>right)x=2*L-2*R-x,vx=-vx;
if(y<under)y=2*R-y,vy=-vy;
if(y>root)y=2*W-2*R-y,vy=-vy;
}
t++;
}
cout<<fixed<<setprecision(2)<<x<<" "<<fixed<<setprecision(2)<<y<<endl;//按照格式输出
}
return 0;
}
方法二:两个方向上的运动是由分别周期的,单方向上碰到墙壁就会逆向返回,因此,我们可以用总路程与单周期长度取模,在判断一开始的速度方向之后,就可以将计算缩短至单周期内(因为经历了整数个周期之后,x或者y回到原来的坐标值,且再次沿着一开始的方向走下去)的运动情况计算,这时候,就不需要再做循环,只需要一步步判断取模后路程的范围,然后算出结果即可(也是类似地利用坐标前后的关系)。这部分公式也请大家自己推导。
c++版本:
#include
using namespace std;
int abj(double a){
return (a>=0?1:-1);//定义判断速度正负函数
}
int main(){
double l,w,x,y,r,a,v,s;double pi=acos(-1);//转化弧度
while(1){
cin>>l>>w>>x>>y>>r>>a>>v>>s;
if(l==0)break;
double vx=v*cos(a*pi/180);
double vy=v*sin(a*pi/180);
double l1=l-r;double w1=w-r;
l-=(2*r);w-=(2*r);x-=r;y-=r;//缩小坐标系 ,以(r,r)为原点建立坐标系
double nx=fmod(fabs(vx*s),2*l);//取模,把移动量限制在单周期长度以内
double ny=fmod(fabs(vy*s),2*w);
if(abj(vx)==1){ //判断正负号,正负号分别处理
if(nx<=l-x)x+=nx;
else if(nx<=2*l-x)x=2*l-nx-x;
else x=nx+x-2*l;
}
if(abj(vx)==-1){
if(nx<=x)x-=nx;
else if(nx<=l+x)x=nx-x;
else x=2*l+x-nx;
}
if(abj(vy)==1){
if(ny<=w-y)y+=ny;
else if(ny<=2*w-y)y=2*w-ny-y;
else y=ny+y-2*w;}
if(abj(vy)==-1){
if(ny<=y)y-=ny;
else if(ny<=w+y)y=ny-y;
else y=2*w+y-ny;
}
x+=r;y+=r;//坐标逆变换
cout<<fixed<<setprecision(2)<<x<<" "<<fixed<<setprecision(2)<<y<<endl;//输出原坐标系下值
}
return 0;
}
方法三:缩小坐标系,我们以(r,r)为原点建立新的坐标系,对l,w做出变换,让他们的值直接就等于球心运动范围内小矩形的长和宽,同时也对x,y做出变换然后,分别对于两个方向,我们都要将其视作从左边界或者下边界出发,即我们一开始就走了x或者y(m),这样去计算他们的虚总位移(即按照方向一直不变走下去变成的位移,这样算出来的值就会是相对与对应边界的位移而不是原来球心位置的位移。),我们随后拿虚总位移的绝对值和单周期长度取余,得到单周期长度内的位移长度,这里一定会是正数,即使原先是朝着负方向的,实际上因为反弹的关系也会变成正向(题目给的大矩形本身就是在第一象限的,如果是其他象限的话,其实可以先全部归结成第一象限的情况,最后再变换处理,这里利用等效的计算结果),随后便与上面同理,分别对特殊情况,即还是再次发生一次反弹的情况做出处理,剩下的直接就是最终结果,最后也别忘了,要变换成原先坐标系之后再输出。
c 语言:
#include
#include
int main(){
double L,W,x,y,R,a,v,s;
double Dx = 0,Dy = 0;
for(int l,w,x0,y0;;){
scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&L,&W,&x,&y,&R,&a,&v,&s);
if(L==0&&W==0&&x==0&&y==0){
break; //输入终止条件
}
a = a/180.0 * acos(-1); //角度转弧度,arcos(-1)==PI
//球转换为质点,缩小球台建立新坐标系
l = L - 2*R;
w = W - 2*R;
x0 = x-R;
y0 = y - R;
Dx = fabs(cos(a)*v*s +x0);
Dy = fabs(sin(a)*v*s + y0);
//求出在新坐标系中质点相对左下角原点的偏移量Dx,Dy
for(;Dx>2*l;){
Dx -= 2*l;
}
for(;Dy>2*w;){
Dy -= 2*w;
}
//去除偏移量中的可能存在的周期部分
if(Dx>l&&Dx<2*l) Dx = 2*l - Dx;
else;//else可以不写
if(Dy>w&&Dy<2*w) Dy = 2*w - Dy;
else;//else可以不写
//求出新坐标系中质点的最终坐标
printf("%.2lf %.2lf\n",Dx + R,Dy + R); //将坐标加上R转换为原坐标系坐标输出
}
return 0;
}
谢谢阅读!
0.0分
1 人评分