答案~~~

思路

图1

观察样例发现要求的曲线长度就是上图的上边界长度。具体来说图形包括每一段路沿法线方向平移 rr 后得到的线段和所有以端点为圆心半径为 r 的圆。

首先想到的是沿着路径模拟。注意到并不是每一段路都有机会和车轮相切(比如说有一个宽度比车轮小很多的深坑),也就是说我们不能仅仅处理相邻两段的交点。我们还会发现路径上的圆弧不仅可以和相邻的线段相接,还可以和不相邻的线段(比如下面这组数据)或其他圆相接。

5 1.00
0.00 0.00
3.00 0.00
4.00 -3.00
5.00 2.00
7.00 2.00

图2

轮子与地面永远是保持垂直的,在每段地面上画一个平行四边形,轮子总是沿着平行四边形的上面的边行走的。计算路面上方轴心位置时,可以根据线段与水平线的夹角与轮子半径以及对应地面点的坐标求得。首先可以将这个轮子轴心前进分为几种情况。

平路、上坡、下坡:从起点上方,一直前进到轮子接触到上升坡或者拐点上方结束,判断轮子轴心结束该端的位置可以在当前地面画一个平行四边形,再在下一个地面画一个平行四边形,轮子总是沿着平行四边形的上面的边行走的,所以求两个平行四边形上面线的交点便是向上转折点,也是轮子前沿刚好接触到上升坡的位置,求两直线交点在后面补充。

向下的转折点:当下个平面与水平线的角度比当前平面低时,会产生向下的转折点,轮子与地面的接触点不变,轴绕接触点旋转,由于轮子始终与地面垂直,所以从一个平面的垂直点旋转到另一平面的旋转点,旋转角度为 180° 两平面下面的夹角,根据半径和角度,计算出弧长,如果夹角大于 180°,那么轮子是可以不用旋转的。

代码

#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;
}

三天三夜……

不苦!但是1求关,2评论,3点赞.

谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值