0
题目描述:
一个大圆(R),套着一个小圆(Rm),有硬币(r)从大圆外出发(审题未注意到这一条件导致多考虑了两种不必要的情况),假设没有摩擦力,碰到小圆就会碰撞反射出来,未碰到小圆则穿过大圆,或者没有进大圆,问硬币在大圆内待得时间是多少。
1
题目分析:
0]
三种情况,如题目描述中所写:碰到小圆就会碰撞反射出来,未碰到小圆则穿过大圆,或者没有进大圆,尤其注意莫忘最后一种。
首先,一定注意题目已经说明,硬币从大圆外出发,不要忘记速度是矢量,既有大小也有方向,大小也不一定是1。
其次,下面两种解法,都要注意硬币有自己的r,题目要求硬币任何部分在大圆内都属于硬币在大圆内的情况,因此要从硬币紧紧挨着大圆外时,开始考虑,那么下面两种方法,对于大圆R和小圆的半径Rm分别相当于变成了R+r,Rm+r。
1]
① 解法①列方程求解[时间作自变量],假设(x1,y1)为硬币出发点,(vx,vy)为速度矢量。
x=x1+vx*t;
y=y1+vy*t;
x*x+y*y=(R+r)*(R+r)
x*x+y*y=(Rm+r)*(Rm+r)
联立上式,得t1,t2,t1m,t2m,分别为出大圆、进大圆、出小圆、进小圆的时刻。解法①注意,方程无解的情况,或者t<0的情况,都是说明硬币没有进大圆,输出0。
② 解法②向量积和sin角度求解长度再除以有方向的速度。假设t是时间,s1是未碰撞情况下总轨迹长度,s2是碰撞情况下轨迹长度,v是速度矢量。假设硬币坐标(px,py),速度矢量为(vx,vy)。注意判断方向等使其出现不进入大圆的情况。
t=s/v;
v=sqrt(vx*vx+vy*vy);
s1=sqrt( (R+r)*(R+r) - d*d );
s2=sqrt( (R+r)*(R+r) - dm*dm ) - sqrt( (Rm+r)*(Rm+r) - dm*dm );
d=sin*(R+r);
dm=sin*(Rm+r);
sin=( (0-px)*(vy) - (0-py)*vx ) / sqrt(px*px + py*py ) / sqrt( vx*vx + vy*vy ):
(另外此处解法②的d的求法中如果仔细探究会发现,sin的求法可能不太好理解,分子使用速度矢量*t来表示那段移动轨迹的矢量,同时分母使用速度矢量的模长(速度的大小)*t来表示那段移动轨迹的矢量大小,最后同时约去了一个t。)
综上,解法②只要列出方程,可以根据两解、一解、无解等情况,来辅助推测题中的坑和陷阱,而解法①需要更清晰的思路,更细致的考虑。
注意学习到,一个点到一条边的距离可以如下计算:
直线L:Ax+By+C=0,直线外点P(Xo,Yo),则,P点到直线L的距离:D=│AXo+BYo+C│/√(A²+B²),
还可用向量叉乘/2得到面积来算,或者向量叉乘得到向量夹角的sin夹角配合斜边来算。
2]
补充关于向量点乘和向量叉乘:
3]
2
解法①
#include <iostream>
#include <math.h>
#include <cmath>
using namespace std;
double Rm,R,r,x,y,vx,vy;
double a,b,c,cm,d,dm;
double t1,t2,t1m,t2m;
int main(){
while(~scanf("%lf%lf%lf%lf%lf%lf%lf",&Rm,&R,&r,&x,&y,&vx,&vy)){
a=vx*vx+vy*vy;
b=2*(x*vx+y*vy);
c=x*x+y*y-(R+r)*(R+r);
cm=x*x+y*y-(Rm+r)*(Rm+r);
d=b*b-4*a*c;
dm=b*b-4*a*cm;
if(a==0||d<=0){//a==0和d<0方程无解,d==0则t1==t2即擦过圆而不进圆
printf("0.000\n");
continue;
}
t1=((-1)*b+sqrt(d))/(2*a);
t2=((-1)*b-sqrt(d))/(2*a);
if(t2<0){//t2<t1,而任意一个t小于0,硬币都是往外走而不可能再进大圆
printf("0.000\n");
continue;
}
if(dm<=0){//dm<=0,硬币穿过大圆而不碰撞小圆
printf("%.3f\n",t1-t2);
continue;
}
t1m=((-1)*b+sqrt(dm))/(2*a);
t2m=((-1)*b-sqrt(dm))/(2*a);//如果能走到这一步,t2一定大于0
printf("%.3f\n",(t1-t1m)+(t2m-t2));
}
}
解法②
#include <iostream>
#include <cmath>
#include <math.h>
using namespace std;
const double eps=1e-8;//PI=acos(-1,0);
double Rm;double R;double r;double x;double y;double vx;double vy;
struct points{
double x;
double y;
}pp,vv;
double t;
double d;
double v;
double len;
int Judge(){//向量点乘(对应x相乘+对应y相乘)(向量内积(向量点乘)和向量外积(向量叉乘),此处是向量点乘,而非向量叉乘)(以(x1,y1)与(x2,y2)为例,向量点乘:x1*x2+y1*y2,向量叉乘:x1*y2-y1*x2=向量(x1,y1)的模 * 向量(x2,y2)的模 * sin(向量夹角))(向量点乘==0,意味着向量夹角为90°,向量点乘小于0,意味着向量夹角大于90°)(向量叉乘=0,意味着sin(向量夹角)==0,则两个向量要么为0°要么为180°),小于0则之间夹角大于90度,等于0则为90度,如题应该严格小于90度才有不为0的答案
struct points vv2;//(硬币起始点指向起点的矢量)
vv2.x=(-1)*(pp.x);
vv2.y=(-1)*(pp.y);
if((vv2.x*vv.x+vv2.y*vv.y)<0||fabs(vv2.x*vv.x+vv2.y*vv.y)<eps){//注意double变量对于==0的判断,用fabs去正负号以及与eps作比较
return 1;///千万不要返回-1,因为如果主程序里if(Judge()),那么-1与1的效果是一样的!!!
}
else{
return 0;
}
}
double get_dis(){
if(fabs(pp.x*vv.y-vv.x*pp.y)<eps){///不要忘记+fabs去正负号
return 0;
}
double sin=fabs((pp.x)*(vv.y)-(pp.y)*(vv.x))/fabs(v)/fabs(len);//向量外积/向量a的模/向量b的模=sin向量夹角,不需要加fabs,但是为了提醒你这里是模长,不同情况不同应对。
return sin*len;
}
int main()
{
while(~scanf("%lf%lf%lf%lf%lf%lf%lf",&Rm,&R,&r,&pp.x,&pp.y,&vv.x,&vv.y)){
len=sqrt(pp.x*pp.x+pp.y*pp.y);//硬币起始坐标连接原点的直线的绝对值距离
v=sqrt(vv.y*vv.y+vv.x*vv.x);//硬币的速度矢量(大小和方向)。
if(Judge()){//方向不对,未曾进大圆
printf("0.000\n");
continue;
}
d=get_dis();//原点到硬币轨迹的直线距离()
//printf("%lf\n",d);
if(d<(R+r)&&d>=(Rm+r)){//从大圆中穿过
t=sqrt((R+r)*(R+r)-(d*d))/v;
printf("%.3f\n",t*2);
}
else if(d<(Rm+r)){//碰撞(包括撞向圆心的那条轨迹)
t=( sqrt((R+r)*(R+r)-d*d) - sqrt((Rm+r)*(Rm+r)-d*d) )/v;
printf("%.3f\n",t*2);
}else{//方向对了,但是距离过远未曾进大圆
printf("0.000\n");
}
}
}