C++实现车轮轨迹
標題:車輪軸跡
棟棟每天騎自行車回家需要經過一條狹長的林蔭道。道路由于年久失修,變得非常不平整。雖然棟棟每次都很顛簸,但他仍把騎車經過林蔭道當成一種樂趣。
由于顛簸,棟棟騎車回家的路徑是一條上下起伏的曲線,棟棟想知道,他回家的這條曲線的長度究竟是多長呢?更準確的,棟棟想知道從林蔭道的起點到林蔭道的終點,他的車前輪的軸(圓心)經過的路徑的長度。
棟棟對路面進行了測量。他把道路簡化成一條條長短不等的直線段,這些直線段首尾相連,且位于同一平面內。并在該平面內建立了一個直角坐標系,把所有線段的端點坐標都計算好。
假設棟棟的自行車在行進的過程中前輪一直是貼著路面前進的。
圖1給出了一個簡單的路面的例子,其中藍色實線為路面,紅色虛線為車輪軸經過的路徑。在這個例子中,棟棟的前輪軸從A點出發,水平走到B點,然后繞著地面的F點到C點(繞出一個圓弧),再沿直線下坡到D點,最后水平走到E點,在這個圖中地面的坐標依次為:(0, 0), (2, 0), (4, -1), (6, -1),前輪半徑為1.50,前輪軸前進的距離依次為:
AB=2.0000;弧長BC=0.6955;CD=1.8820;DE=1.6459。
總長度為6.2233。
圖2給出了一個較為復雜的路面的例子,在這個例子中,車輪在第一個下坡還沒下完時(D點)就開始上坡了,之后在坡的頂點要從E繞一個較大的圓弧到F點。這個圖中前輪的半徑為1,每一段的長度依次為:
AB=3.0000;弧長BC=0.9828;CD=1.1913;DE=2.6848;弧長EF=2.6224; FG=2.4415;GH=2.2792。
總長度為15.2021。
現在給出了車輪的半徑和路面的描述,請求出車輪軸軌跡的總長度。
輸入的第一行包含一個整數n和一個實數r,用一個空格分隔,表示描述路面的坐標點數和車輪的半徑。
接下來n行,每個包含兩個實數,其中第i行的兩個實數x[i], y[i]表示描述路面的第i個點的坐標。
路面定義為所有路面坐標點順次連接起來的折線。給定的路面的一定滿足以下性質:
*第一個坐標點一定是(0, 0);
*第一個點和第二個點的縱坐標相同;
*倒數第一個點和倒數第二個點的縱坐標相同;
*第一個點和第二個點的距離不少于車輪半徑;
*倒數第一個點和倒數第二個點的的距離不少于車輪半徑;
*后一個坐標點的橫坐標大于前一個坐標點的橫坐標,即對于所有的i,x[i+1]>x[i]。
輸出一個實數,四舍五入保留兩個小數,表示車輪軸經過的總長度。
你的結果必須和參考答案一模一樣才能得分。數據保證答案精確值的小數點后第三位不是4或5。
【樣例輸入1】
4 1.50
0.00 0.00
2.00 0.00
4.00 -1.00
6.00 -1.00
【樣例輸出1】
6.22
【樣例說明1】
這個樣例對應圖1。
【樣例輸入2】
6 1.00
0.00 0.00
3.00 0.00
5.00 -3.00
6.00 2.00
7.00 -1.00
10.00 -1.00
【樣例輸出2】
15.20
【樣例說明2】
這個樣例對應圖2
【數據規模與約定】
對于20%的數據,n=4;
對于40%的數據,n≤10;
對于100%的數據,4≤n≤100,0.5≤r≤20.0,x[i] ≤2000.0,-2000.0≤y[i] ≤2000.0。
資源約定:
峰值內存消耗 < 64M
CPU消耗 < 1000ms
請嚴格按要求輸出,不要畫蛇添足地打印類似:“請您輸入…” 的多余內容。
所有代碼放在同一個源文件中,調試通過后,拷貝提交該源碼。
注意: main函數需要返回0
注意: 只使用ANSI C/ANSI C++ 標準,不要調用依賴于編譯環境或操作系統的特殊函數。
注意: 所有依賴的函數必須明確地在源文件中 #include , 不能通過工程設置而省略常用頭文件。
提交時,注意選擇所期望的編譯器類型(千萬不要混淆c和cpp)。
錦囊有20%的數據n=4,此時路面由兩段水平線和一個坡組成,可以分為上坡和下坡討論,可以推導出數學公式,這20%的分數比較好拿。
此題正確的做法是:對于每個條線段,將這條線段按車輪的半徑平移到路面上方,然后在每個線段的端點畫一個圓。由這些平移后的線段和圓組成的最上面的一條線就是車輪的路徑,接下來需要算出這條線。
這條線是一條分段連續的線,每段或者是一條直線段,或者是一段圓弧。先求出所有直線段與直線段、直線段與圓、圓與圓的交點,并把所有的端點,所有圓的左右邊界一起放入一個集合中,這個集合中的所有橫坐標就是可能分段的坐標。按這個集合的坐標分段,每一段分別計算。每一段只需要找到最靠上方的線段/弧即可,最終將所有段的線段和弧的長度相加得到答案。
本題與幾何關系較大,公式推導是一個麻煩之處。另外選手可能漏考慮一些情況而失分。本題對編程的要求較高,較容易寫錯,而且不好調試。
#include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <vector> #include <cmath> #include <algorithm>using namespace std;const int MAXN = 10000; const double PI = atan(1.0) * 4; const double EPS = 1e-10;class Point { public:double x, y;Point() {}Point(double x, double y) : x(x), y(y) {}Point operator - (const Point &r) const { return Point(x-r.x, y-r.y); }Point operator + (const Point &r) const { return Point(x+r.x, y+r.y); }Point &operator += (const Point &r) { x += r.x; y += r.y; return *this; }Point &operator *= (double m) { x *= m; y *= m; return *this; }Point pOfRotate(double angle) const {double cosA = cos(angle);double sinA = sin(angle);return Point(cosA*x-sinA*y, sinA*x+cosA*y);}Point pOfRotate90() const { return Point(-y, x); }double length() const { return sqrt(x*x+y*y); }Point pOfNormal() const {double len = length();return Point(x/len, y/len);}double angle() const { return atan2(y, x); } };ostream & operator <<(ostream &os, const Point &v) {os << "(" << v.x << "," << v.y << ")";return os; }class Segment; class Circle;class Seg { public:virtual double getLeft() const = 0;virtual double getRight() const = 0;virtual double getY(double x) const = 0;virtual double getLength(double x1, double x2) const = 0;virtual void intersect(Seg *r) const = 0;virtual void intersect(const Segment &v) const = 0;virtual void intersect(const Circle &v) const = 0;bool contains(double x) const { return x>=getLeft() && x<=getRight(); }virtual void acceptPrint(ostream &os) const = 0; };ostream & operator <<(ostream &os, const Seg &v) {v.acceptPrint(os);return os; }Point intersectRet[4]; int tIntersectRet;class Segment : public Seg { public:Point a, b;Segment &moveLeft(double dis){Point tmp = ((b-a).pOfRotate90().pOfNormal() *= dis);a += tmp;b += tmp;return *this;}virtual double getLeft() const { return a.x; }virtual double getRight() const { return b.x; }virtual double getY(double x) const {return (x-a.x)*(b.y-a.y)/(b.x-a.x)+a.y;}virtual double getLength(double x1, double x2) const {return (x2-x1) * (b-a).length() / (b.x-a.x);}virtual void intersect(Seg *r) const {r->intersect(*this);}virtual void intersect(const Segment &v) const {tIntersectRet = 0;double ang = (b-a).angle();Point c = (v.a-a).pOfRotate(-ang);Point d = (v.b-a).pOfRotate(-ang);// Bug//double di = b.length();double di = (b-a).length();if (!((c.y>0&&d.y<0) || (c.y<0&&d.y>0)))return ;double x = (d.x-c.x) * (-c.y) / (d.y-c.y) + c.x;if (x<0 || x>di)return ;Point ret = Point(x,0).pOfRotate(ang)+a;intersectRet[tIntersectRet++] = ret;}virtual void intersect(const Circle &v) const;virtual void acceptPrint(ostream &os) const {os << a << "-" << b;} };class Circle : public Seg { public:Point c;double r;virtual double getLeft() const { return c.x - r; }virtual double getRight() const { return c.x + r; }virtual double getY(double x) const {double y2 = r * r - (c.x - x) * (c.x - x);if (y2<0) y2 = 0;return c.y + sqrt(y2);}virtual double getLength(double x1, double x2) const {x1 -= c.x; x2 -= c.x;double a1 = Point(x1, sqrt(abs(r*r-x1*x1))).angle(), a2 = Point(x2, sqrt(abs(r*r-x2*x2))).angle();return (a1-a2) * r;}virtual void intersect(Seg *r) const {r->intersect(*this);}virtual void intersect(const Segment &v) const {tIntersectRet = 0;Point a = v.a - c;Point b = v.b - c;double ang = (b-a).angle();Point nA = a.pOfRotate(-ang);Point nB = b.pOfRotate(-ang);double y = nA.y;if (y>r || y<-r)return ;double x = sqrt(r*r - y*y);if (x>=nA.x && x<=nB.x)intersectRet[tIntersectRet++] = Point(x, y).pOfRotate(ang) + c;if (-x>=nA.x && -x<=nB.x)intersectRet[tIntersectRet++] = Point(-x, y).pOfRotate(ang) + c;}virtual void intersect(const Circle &v) const {tIntersectRet = 0;Point p = v.c - c;double d = p.length();if (d > r + v.r || d==0)return ;double x = (r*r - v.r*v.r + d*d) / (2*d);if (x <= r){double y = sqrt(abs(r*r - x*x));double ang = p.angle();intersectRet[tIntersectRet++] = Point(x,y).pOfRotate(ang) + c;intersectRet[tIntersectRet++] = Point(x,-y).pOfRotate(ang) + c;}}virtual void acceptPrint(ostream &os) const {os << c << "," << r;} };void Segment::intersect(const Circle &v) const {v.intersect(*this); }int n; Point inps[MAXN]; vector<Seg *> segs; vector<double> spes; double radius = 1;void input() {scanf("%d%lf", &n, &radius);for (int i = 0; i < n; ++i){double x, y;scanf("%lf%lf", &x, &y);inps[i] = Point(x, y);} }void process() {segs.clear();spes.clear();for (int i = 1; i + 1 < n; ++i){Circle *tmp = new Circle;tmp->c = inps[i];tmp->r = radius;segs.push_back(tmp);}for (int i = 0; i + 1 < n; ++i){Segment *tmp = new Segment;tmp->a = inps[i];tmp->b = inps[i+1];tmp->moveLeft(radius);segs.push_back(tmp);}for (int i = 0; i < (int)segs.size(); ++i){spes.push_back(segs[i]->getLeft());spes.push_back(segs[i]->getRight());}for (int i = 0; i < (int)segs.size(); ++i){for (int j = i+1; j < (int)segs.size(); ++j){segs[i]->intersect(segs[j]);if (tIntersectRet > 0){for (int id = 0; id < tIntersectRet; ++id){//cout << *segs[i] << " " << *segs[j] << " : " << intersectRet[id] << endl;spes.push_back(intersectRet[id].x);}}}}sort(spes.begin(), spes.end());double pre = spes[0];const double NONE = 1e30;double preEnd = NONE;double totalLen = 0;for (int i = 1; i < (int)spes.size(); ++i){if (spes[i]-pre < EPS)continue;double cur = (pre+spes[i]) / 2;//cout << "Processing " << cur << " from " << pre << " to " << spes[i] << endl;if (cur>=inps[0].x && cur<=inps[n-1].x){double MY = -NONE;int who;for (int j = 0; j < (int)segs.size(); ++j){if (!segs[j]->contains(cur))continue;double y = segs[j]->getY(cur);if (y > MY){MY = y;who = j;}}if (preEnd != NONE){double LY = segs[who]->getY(pre);//cout << "Drop info " << *segs[who] << " " << "[" << pre << "]" << endl;totalLen += abs(preEnd-LY);//cout << "Pre drop = " << abs(preEnd-LY) << " from " << preEnd << " to " << LY << endl;}double len = segs[who]->getLength(pre, spes[i]);if (len < 0)printf("Error!\n");//cout << "Curlen = " << len << " from " << pre << " to " << spes[i] << endl;totalLen += len;preEnd = segs[who]->getY(spes[i]);}pre = spes[i];}printf("%0.2lf\n", totalLen);for (int i = 0; i < (int)segs.size(); ++i)delete segs[i];segs.clear(); }int main() {input();process();return 0; }總結
- 上一篇: 栈和队列OJ练习——栈实现队列,队列实现
- 下一篇: VINS-MONO边缘化策略