題目鏈接:點擊查看
題目大意:在一個二維平面上,給出一個實心圓柱體,再給出一個可以視為質點的小球,小球初始時位于點 A ,會給出一個速度向量 VA,當小球碰到圓柱體時,會因碰撞彈開,規定碰撞不會丟失動能,問在小球在運動的過程中能不能到達點 B
題目分析:稍微畫一下圖:
為了方便表示射線,我在無窮遠處計算出了一個點 inf,這樣射線 A?就轉換為了線段 ( A , inf ),同理圖中的 l1 和 l3 都是以線段表示的射線,圖中點 P 表示為圓心,G1 和 G2 表示 l1 與圓的兩個交點,規定 G1 距離點 A 更近,當我們計算出 G1 后,那么直線 ( P , G1 ) 就是法線了,將點 A 根據法線對稱得到點 AA,這樣就能計算出小球碰撞后的路線了,也就是 l3 所表示的射線,此時只需要分兩種情況討論即可:
如果 l1 與圓不相交(相切或相離),只需要判斷一下點 B 是否在 l1 上即可如果 l1 與圓相交,求出距離點 A 較近的交點 G1,一頓操作計算出 l2 和 l3 ,判斷點 B 是否位于其中一條線段上即可
代碼:
?
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
#define double long double
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e5+100;// `計算幾何模板`
const double eps = 1e-8;
const double pi = acos(-1.0);
const int maxp = 1010;
//`Compares a double to zero`
int sgn(double x){if(fabs(x) < eps)return 0;if(x < 0)return -1;else return 1;
}
//square of a double
inline double sqr(double x){return x*x;}
/** Point* Point() - Empty constructor* Point(double _x,double _y) - constructor* input() - double input* output() - %.2f output* operator == - compares x and y* operator < - compares first by x, then by y* operator - - return new Point after subtracting curresponging x and y* operator ^ - cross product of 2d points* operator * - dot product* len() - gives length from origin* len2() - gives square of length from origin* distance(Point p) - gives distance from p* operator + Point b - returns new Point after adding curresponging x and y* operator * double k - returns new Point after multiplieing x and y by k* operator / double k - returns new Point after divideing x and y by k* rad(Point a,Point b)- returns the angle of Point a and Point b from this Point* trunc(double r) - return Point that if truncated the distance from center to r* rotleft() - returns 90 degree ccw rotated point* rotright() - returns 90 degree cw rotated point* rotate(Point p,double angle) - returns Point after rotateing the Point centering at p by angle radian ccw*/
struct Point{double x,y;Point(){}Point(double _x,double _y){x = _x;y = _y;}void input(){cin>>x>>y;
// scanf("%lf%lf",&x,&y);}void output(){printf("%.2f %.2f\n",x,y);}bool operator == (Point b)const{return sgn(x-b.x) == 0 && sgn(y-b.y) == 0;}bool operator < (Point b)const{return sgn(x-b.x)== 0?sgn(y-b.y)<0:x<b.x;}Point operator -(const Point &b)const{return Point(x-b.x,y-b.y);}//叉積double operator ^(const Point &b)const{return x*b.y - y*b.x;}//點積double operator *(const Point &b)const{return x*b.x + y*b.y;}//返回長度double len(){return hypot(x,y);//庫函數}//返回長度的平方double len2(){return x*x + y*y;}//返回兩點的距離double distance(Point p){return hypot(x-p.x,y-p.y);}Point operator +(const Point &b)const{return Point(x+b.x,y+b.y);}Point operator *(const double &k)const{return Point(x*k,y*k);}Point operator /(const double &k)const{return Point(x/k,y/k);}//`計算pa 和 pb 的夾角`//`就是求這個點看a,b 所成的夾角`//`測試 LightOJ1203`double rad(Point a,Point b){Point p = *this;return fabs(atan2( fabs((a-p)^(b-p)),(a-p)*(b-p) ));}//`化為長度為r的向量`Point trunc(double r){double l = len();if(!sgn(l))return *this;r /= l;return Point(x*r,y*r);}//`逆時針旋轉90度`Point rotleft(){return Point(-y,x);}//`順時針旋轉90度`Point rotright(){return Point(y,-x);}//`繞著p點逆時針旋轉angle`Point rotate(Point p,double angle){Point v = (*this) - p;double c = cos(angle), s = sin(angle);return Point(p.x + v.x*c - v.y*s,p.y + v.x*s + v.y*c);}
};
/** Stores two points* Line() - Empty constructor* Line(Point _s,Point _e) - Line through _s and _e* operator == - checks if two points are same* Line(Point p,double angle) - one end p , another end at angle degree* Line(double a,double b,double c) - Line of equation ax + by + c = 0* input() - inputs s and e* adjust() - orders in such a way that s < e* length() - distance of se* angle() - return 0 <= angle < pi* relation(Point p) - 3 if point is on line* 1 if point on the left of line* 2 if point on the right of line* pointonseg(double p) - return true if point on segment* parallel(Line v) - return true if they are parallel* segcrossseg(Line v) - returns 0 if does not intersect* returns 1 if non-standard intersection* returns 2 if intersects* linecrossseg(Line v) - line and seg* linecrossline(Line v) - 0 if parallel* 1 if coincides* 2 if intersects* crosspoint(Line v) - returns intersection point* dispointtoline(Point p) - distance from point p to the line* dispointtoseg(Point p) - distance from p to the segment* dissegtoseg(Line v) - distance of two segment* lineprog(Point p) - returns projected point p on se line* symmetrypoint(Point p) - returns reflection point of p over se**/
struct Line{Point s,e;Line(){}Line(Point _s,Point _e){s = _s;e = _e;}bool operator ==(Line v){return (s == v.s)&&(e == v.e);}//`根據一個點和傾斜角angle確定直線,0<=angle<pi`Line(Point p,double angle){s = p;if(sgn(angle-pi/2) == 0){e = (s + Point(0,1));}else{e = (s + Point(1,tan(angle)));}}//ax+by+c=0Line(double a,double b,double c){if(sgn(a) == 0){s = Point(0,-c/b);e = Point(1,-c/b);}else if(sgn(b) == 0){s = Point(-c/a,0);e = Point(-c/a,1);}else{s = Point(0,-c/b);e = Point(1,(-c-a)/b);}}void input(){s.input();e.input();}void adjust(){if(e < s)swap(s,e);}//求線段長度double length(){return s.distance(e);}//`返回直線傾斜角 0<=angle<pi`double angle(){double k = atan2(e.y-s.y,e.x-s.x);if(sgn(k) < 0)k += pi;if(sgn(k-pi) == 0)k -= pi;return k;}//`點和直線關系`//`1 在左側`//`2 在右側`//`3 在直線上`int relation(Point p){int c = sgn((p-s)^(e-s));if(c < 0)return 1;else if(c > 0)return 2;else return 3;}// 點在線段上的判斷bool pointonseg(Point p){return sgn((p-s)^(e-s)) == 0 && sgn((p-s)*(p-e)) <= 0;}//`兩向量平行(對應直線平行或重合)`bool parallel(Line v){return sgn((e-s)^(v.e-v.s)) == 0;}//`兩線段相交判斷`//`2 規范相交`//`1 非規范相交`//`0 不相交`int segcrossseg(Line v){int d1 = sgn((e-s)^(v.s-s));int d2 = sgn((e-s)^(v.e-s));int d3 = sgn((v.e-v.s)^(s-v.s));int d4 = sgn((v.e-v.s)^(e-v.s));if( (d1^d2)==-2 && (d3^d4)==-2 )return 2;return (d1==0 && sgn((v.s-s)*(v.s-e))<=0) ||(d2==0 && sgn((v.e-s)*(v.e-e))<=0) ||(d3==0 && sgn((s-v.s)*(s-v.e))<=0) ||(d4==0 && sgn((e-v.s)*(e-v.e))<=0);}//`直線和線段相交判斷`//`-*this line -v seg`//`2 規范相交`//`1 非規范相交`//`0 不相交`int linecrossseg(Line v){int d1 = sgn((e-s)^(v.s-s));int d2 = sgn((e-s)^(v.e-s));if((d1^d2)==-2) return 2;return (d1==0||d2==0);}//`兩直線關系`//`0 平行`//`1 重合`//`2 相交`int linecrossline(Line v){if((*this).parallel(v))return v.relation(s)==3;return 2;}//`求兩直線的交點`//`要保證兩直線不平行或重合`Point crosspoint(Line v){double a1 = (v.e-v.s)^(s-v.s);double a2 = (v.e-v.s)^(e-v.s);return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));}//點到直線的距離double dispointtoline(Point p){return fabs((p-s)^(e-s))/length();}//點到線段的距離double dispointtoseg(Point p){if(sgn((p-s)*(e-s))<0 || sgn((p-e)*(s-e))<0)return min(p.distance(s),p.distance(e));return dispointtoline(p);}//`返回線段到線段的距離`//`前提是兩線段不相交,相交距離就是0了`double dissegtoseg(Line v){return min(min(dispointtoseg(v.s),dispointtoseg(v.e)),min(v.dispointtoseg(s),v.dispointtoseg(e)));}//`返回點p在直線上的投影`Point lineprog(Point p){return s + ( ((e-s)*((e-s)*(p-s)))/((e-s).len2()) );}//`返回點p關于直線的對稱點`Point symmetrypoint(Point p){Point q = lineprog(p);return Point(2*q.x-p.x,2*q.y-p.y);}
};
//圓
struct circle{Point p;//圓心double r;//半徑circle(){}circle(Point _p,double _r){p = _p;r = _r;}circle(double x,double y,double _r){p = Point(x,y);r = _r;}//`三角形的外接圓`//`需要Point的+ / rotate() 以及Line的crosspoint()`//`利用兩條邊的中垂線得到圓心`//`測試:UVA12304`circle(Point a,Point b,Point c){Line u = Line((a+b)/2,((a+b)/2)+((b-a).rotleft()));Line v = Line((b+c)/2,((b+c)/2)+((c-b).rotleft()));p = u.crosspoint(v);r = p.distance(a);}//`三角形的內切圓`//`參數bool t沒有作用,只是為了和上面外接圓函數區別`//`測試:UVA12304`circle(Point a,Point b,Point c,bool t){Line u,v;double m = atan2(b.y-a.y,b.x-a.x), n = atan2(c.y-a.y,c.x-a.x);u.s = a;u.e = u.s + Point(cos((n+m)/2),sin((n+m)/2));v.s = b;m = atan2(a.y-b.y,a.x-b.x) , n = atan2(c.y-b.y,c.x-b.x);v.e = v.s + Point(cos((n+m)/2),sin((n+m)/2));p = u.crosspoint(v);r = Line(a,b).dispointtoseg(p);}//輸入void input(){p.input();scanf("%lf",&r);}//輸出void output(){printf("%.2lf %.2lf %.2lf\n",p.x,p.y,r);}bool operator == (circle v){return (p==v.p) && sgn(r-v.r)==0;}bool operator < (circle v)const{return ((p<v.p)||((p==v.p)&&sgn(r-v.r)<0));}//面積double area(){return pi*r*r;}//周長double circumference(){return 2*pi*r;}//`點和圓的關系`//`0 圓外`//`1 圓上`//`2 圓內`int relation(Point b){double dst = b.distance(p);if(sgn(dst-r) < 0)return 2;else if(sgn(dst-r)==0)return 1;return 0;}//`線段和圓的關系`//`比較的是圓心到線段的距離和半徑的關系`int relationseg(Line v){double dst = v.dispointtoseg(p);if(sgn(dst-r) < 0)return 2;else if(sgn(dst-r) == 0)return 1;return 0;}//`直線和圓的關系`//`比較的是圓心到直線的距離和半徑的關系`int relationline(Line v){double dst = v.dispointtoline(p);if(sgn(dst-r) < 0)return 2;else if(sgn(dst-r) == 0)return 1;return 0;}//`兩圓的關系`//`5 相離`//`4 外切`//`3 相交`//`2 內切`//`1 內含`//`需要Point的distance`//`測試:UVA12304`int relationcircle(circle v){double d = p.distance(v.p);if(sgn(d-r-v.r) > 0)return 5;if(sgn(d-r-v.r) == 0)return 4;double l = fabs(r-v.r);if(sgn(d-r-v.r)<0 && sgn(d-l)>0)return 3;if(sgn(d-l)==0)return 2;if(sgn(d-l)<0)return 1;}//`求兩個圓的交點,返回0表示沒有交點,返回1是一個交點,2是兩個交點`//`需要relationcircle`//`測試:UVA12304`int pointcrosscircle(circle v,Point &p1,Point &p2){int rel = relationcircle(v);if(rel == 1 || rel == 5)return 0;double d = p.distance(v.p);double l = (d*d+r*r-v.r*v.r)/(2*d);double h = sqrt(r*r-l*l);Point tmp = p + (v.p-p).trunc(l);p1 = tmp + ((v.p-p).rotleft().trunc(h));p2 = tmp + ((v.p-p).rotright().trunc(h));if(rel == 2 || rel == 4)return 1;return 2;}//`求直線和圓的交點,返回交點個數`int pointcrossline(Line v,Point &p1,Point &p2){if(!(*this).relationline(v))return 0;Point a = v.lineprog(p);double d = v.dispointtoline(p);d = sqrt(r*r-d*d);if(sgn(d) == 0){p1 = a;p2 = a;return 1;}p1 = a + (v.e-v.s).trunc(d);p2 = a - (v.e-v.s).trunc(d);return 2;}//`得到過a,b兩點,半徑為r1的兩個圓`int gercircle(Point a,Point b,double r1,circle &c1,circle &c2){circle x(a,r1),y(b,r1);int t = x.pointcrosscircle(y,c1.p,c2.p);if(!t)return 0;c1.r = c2.r = r;return t;}//`得到與直線u相切,過點q,半徑為r1的圓`//`測試:UVA12304`int getcircle(Line u,Point q,double r1,circle &c1,circle &c2){double dis = u.dispointtoline(q);if(sgn(dis-r1*2)>0)return 0;if(sgn(dis) == 0){c1.p = q + ((u.e-u.s).rotleft().trunc(r1));c2.p = q + ((u.e-u.s).rotright().trunc(r1));c1.r = c2.r = r1;return 2;}Line u1 = Line((u.s + (u.e-u.s).rotleft().trunc(r1)),(u.e + (u.e-u.s).rotleft().trunc(r1)));Line u2 = Line((u.s + (u.e-u.s).rotright().trunc(r1)),(u.e + (u.e-u.s).rotright().trunc(r1)));circle cc = circle(q,r1);Point p1,p2;if(!cc.pointcrossline(u1,p1,p2))cc.pointcrossline(u2,p1,p2);c1 = circle(p1,r1);if(p1 == p2){c2 = c1;return 1;}c2 = circle(p2,r1);return 2;}//`同時與直線u,v相切,半徑為r1的圓`//`測試:UVA12304`int getcircle(Line u,Line v,double r1,circle &c1,circle &c2,circle &c3,circle &c4){if(u.parallel(v))return 0;//兩直線平行Line u1 = Line(u.s + (u.e-u.s).rotleft().trunc(r1),u.e + (u.e-u.s).rotleft().trunc(r1));Line u2 = Line(u.s + (u.e-u.s).rotright().trunc(r1),u.e + (u.e-u.s).rotright().trunc(r1));Line v1 = Line(v.s + (v.e-v.s).rotleft().trunc(r1),v.e + (v.e-v.s).rotleft().trunc(r1));Line v2 = Line(v.s + (v.e-v.s).rotright().trunc(r1),v.e + (v.e-v.s).rotright().trunc(r1));c1.r = c2.r = c3.r = c4.r = r1;c1.p = u1.crosspoint(v1);c2.p = u1.crosspoint(v2);c3.p = u2.crosspoint(v1);c4.p = u2.crosspoint(v2);return 4;}//`同時與不相交圓cx,cy相切,半徑為r1的圓`//`測試:UVA12304`int getcircle(circle cx,circle cy,double r1,circle &c1,circle &c2){circle x(cx.p,r1+cx.r),y(cy.p,r1+cy.r);int t = x.pointcrosscircle(y,c1.p,c2.p);if(!t)return 0;c1.r = c2.r = r1;return t;}//`過一點作圓的切線(先判斷點和圓的關系)`//`測試:UVA12304`int tangentline(Point q,Line &u,Line &v){int x = relation(q);if(x == 2)return 0;if(x == 1){u = Line(q,q + (q-p).rotleft());v = u;return 1;}double d = p.distance(q);double l = r*r/d;double h = sqrt(r*r-l*l);u = Line(q,p + ((q-p).trunc(l) + (q-p).rotleft().trunc(h)));v = Line(q,p + ((q-p).trunc(l) + (q-p).rotright().trunc(h)));return 2;}//`求兩圓相交的面積`double areacircle(circle v){int rel = relationcircle(v);if(rel >= 4)return 0.0;if(rel <= 2)return min(area(),v.area());double d = p.distance(v.p);double hf = (r+v.r+d)/2.0;double ss = 2*sqrt(hf*(hf-r)*(hf-v.r)*(hf-d));double a1 = acos((r*r+d*d-v.r*v.r)/(2.0*r*d));a1 = a1*r*r;double a2 = acos((v.r*v.r+d*d-r*r)/(2.0*v.r*d));a2 = a2*v.r*v.r;return a1+a2-ss;}//`求圓和三角形pab的相交面積`//`測試:POJ3675 HDU3982 HDU2892`double areatriangle(Point a,Point b){if(sgn((p-a)^(p-b)) == 0)return 0.0;Point q[5];int len = 0;q[len++] = a;Line l(a,b);Point p1,p2;if(pointcrossline(l,q[1],q[2])==2){if(sgn((a-q[1])*(b-q[1]))<0)q[len++] = q[1];if(sgn((a-q[2])*(b-q[2]))<0)q[len++] = q[2];}q[len++] = b;if(len == 4 && sgn((q[0]-q[1])*(q[2]-q[1]))>0)swap(q[1],q[2]);double res = 0;for(int i = 0;i < len-1;i++){if(relation(q[i])==0||relation(q[i+1])==0){double arg = p.rad(q[i],q[i+1]);res += r*r*arg/2.0;}else{res += fabs((q[i]-p)^(q[i+1]-p))/2.0;}}return res;}
};int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);int w;cin>>w;int kase=0;while(w--){bool flag;Point P,A,B,VA,G1,G2;double r;P.input();cin>>r;A.input();VA.input();B.input();circle C(P,r);Line l1(A,A+VA*1e4);if(C.relationseg(l1)==2){C.pointcrossline(l1,G2,G1);Point AA=Line(G1,P).symmetrypoint(A);Line l2(A,G1);Line l3(G1,G1+((AA-G1)*1e4));flag=l2.pointonseg(B)||l3.pointonseg(B);}else{flag=l1.pointonseg(B);}printf("Case #%d: %s\n",++kase,flag?"Yes":"No");}return 0;
}
?
總結
以上是生活随笔為你收集整理的HDU - 5572 An Easy Physics Problem(几何-碰撞问题)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。