Hdu 4667 Building Fence

Building Fence

这个题有两种解法:

方法一:将圆均分2000个点,暴力水过

程序简单,好写

精度要求比较高,无从下手的时候可以尝试一下,一旦精度要求比较高不过的可能性比较大...

交c++超时,g++800+ms,表示不理解

const int INF = 1000000000;
const double eps = 1e-10;
const int MOD = 100000007;
const int MAXN = 10000000;
const double PI = acos(-1.0);


///*************基础***********/
inline int dcmp(double x)
{
    if(fabs(x) < eps) return 0;
    else return x < 0 ? -1 : 1;
}
struct Point
{
    double x, y;
    Point(double x=0, double y=0):x(x),y(y) { }
    inline void read() { scanf("%lf%lf", &x, &y); }
};
typedef vector<Point> Polygon;

typedef Point Vector;

inline Vector operator + (Vector A, Vector B) { return Vector(A.x+B.x, A.y+B.y); }
inline Vector operator - (Point A, Point B) { return Vector(A.x-B.x, A.y-B.y); }
inline Vector operator * (Vector A, double p) { return Vector(A.x*p, A.y*p); }
inline Vector operator / (Vector A, double p) { return Vector(A.x/p, A.y/p); }

inline bool operator < (const Point& a, const Point& b)
{
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}

inline bool operator == (const Point& a, const Point &b)
{
    return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}

inline double Dot(Vector A, Vector B)
{
    return A.x*B.x + A.y*B.y;
}
inline double Length(Vector A)
{
    return sqrt(Dot(A, A));
}
inline double Angle(Vector A, Vector B)
{
    return acos(Dot(A, B) / Length(A) / Length(B));
}
inline double angle(Vector v)
{
    return atan2(v.y, v.x);
}
inline double Cross(Vector A, Vector B)
{
    return A.x*B.y - A.y*B.x;
}
inline Vector vecunit(Vector x)
{
    return x / Length(x);   //单位向量
}
inline Vector Normal(Vector x)
{
    return Point(-x.y, x.x) / Length(x);   //垂直法向量
}
inline Vector Rotate(Vector A, double rad)
{
    return Vector(A.x*cos(rad)-A.y*sin(rad), A.x*sin(rad)+A.y*cos(rad));
}



// 点集凸包
// 如果不希望在凸包的边上有输入点,把两个 <= 改成 <
// 注意:输入点集会被修改
vector<Point> ConvexHull(vector<Point>& p)
{
    // 预处理,删除重复点
    sort(p.begin(), p.end());
    p.erase(unique(p.begin(), p.end()), p.end());

    int n = p.size();
    int m = 0;
    vector<Point> ch(n+1);
    for(int i = 0; i < n; i++)
    {
        while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
        ch[m++] = p[i];
    }
    int k = m;
    for(int i = n-2; i >= 0; i--)
    {
        while(m > k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
        ch[m++] = p[i];
    }
    if(n > 1) m--;
    ch.resize(m);
    return ch;
}


inline double dist(Point a, Point b)
{
    return Length(a - b);
}


/************圆************/
struct Circle
{
    Point c;
    double r;
    Circle() {}
    Circle(Point c, double r):c(c), r(r) {}
    inline Point point(double a) //根据圆心角求点坐标
    {
        return Point(c.x+cos(a)*r, c.y+sin(a)*r);
    }
};


vector<Point> p, ans;
const double interval = PI / 1000;

int main()
{
//    freopen("0.txt", "r", stdin);
    int N, M, size;
    Point input;
    double r, tx, ty, dis, ang;
    while (~RII(N, M))
    {
        ans.clear(), p.clear();
        dis = 0;
        REP(i, N)
        {
            scanf("%lf%lf%lf", &tx, &ty, &r);
            for (double ang = 0; ang < 2 * PI; ang += interval)
            {
                input.x = tx + cos(ang) * r;
                input.y = ty + sin(ang) * r;
                p.push_back(input);
            }
        }
        REP(i, M)
        {
            input.read();
            p.push_back(input);
            input.read();
            p.push_back(input);
            input.read();
            p.push_back(input);
        }
        ans = ConvexHull(p);
        size = ans.size();
        REP(i, size - 1)
        {
            dis += Length(ans[i] - ans[i + 1]);
        }
        dis += dist(ans[size - 1], ans[0]);
        printf("%.5lf\n", dis);
    }
    return 0;
}

方法二:

将有可能构成凸包的点计算出来,包括 三角形的顶点,三角形顶点与各个圆的切点,任意两个圆的切点 

然后计算凸包即可

有两个要注意的地方,第一是求凸包边长时候,需要判断一下当前计算的两个点是否在同一个圆上,若是则需要计算圆弧长度

                                     第二是只有一个圆的时候需要特判

const int INF = 0x3f3f3f3f;
const double eps = 1e-10;
const int MOD = 100000007;
const int MAXN = 1000010;
const double PI = acos(-1.0);


///*************基础***********/
double torad(double deg)
{
    return deg / 180 * PI;
}
inline int dcmp(double x)
{
    if(fabs(x) < eps) return 0;
    else return x < 0 ? -1 : 1;
}
struct Point
{
    double x, y;
    Point(double x=0, double y=0):x(x),y(y) { }
    inline void read()
    {
        scanf("%lf%lf", &x, &y);
    }
};
typedef vector<Point> Polygon;
typedef Point Vector;

inline Vector operator + (Vector A, Vector B)
{
    return Vector(A.x+B.x, A.y+B.y);
}
inline Vector operator - (Point A, Point B)
{
    return Vector(A.x-B.x, A.y-B.y);
}
inline Vector operator * (Vector A, double p)
{
    return Vector(A.x*p, A.y*p);
}
inline Vector operator / (Vector A, double p)
{
    return Vector(A.x/p, A.y/p);
}


inline bool operator < (Point a, Point b)
{
    return a.x < b.x || (a.x == b.x && a.y < b.y);
}


inline bool operator == (Point a, Point b)
{
    return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}


inline double Dot(Vector A, Vector B)
{
    return A.x*B.x + A.y*B.y;
}
inline double Length(Vector A)
{
    return sqrt(Dot(A, A));
}
inline double Angle(Vector A, Vector B)
{
    return acos(Dot(A, B) / Length(A) / Length(B));
}
inline double angle(Vector v)
{
    return atan2(v.y, v.x);
}
inline double Cross(Vector A, Vector B)
{
    return A.x*B.y - A.y*B.x;
}
inline Vector Unit(Vector x)
{
    return x / Length(x);   //单位向量
}
inline Vector Normal(Vector x)
{
    return Point(-x.y, x.x) / Length(x);   //垂直法向量
}
inline Vector Rotate(Vector A, double rad)
{
    return Vector(A.x*cos(rad)-A.y*sin(rad), A.x*sin(rad)+A.y*cos(rad));
}
inline double Area2(Point A, Point B, Point C)
{
    return Cross(B-A, C-A);
}

template <class T> T sqr(T x)
{
    return x * x ;
}

// 点集凸包
// 如果不希望在凸包的边上有输入点,把两个 <= 改成 <
// 注意:输入点集会被修改
vector<Point> ConvexHull(vector<Point>& p)
{
    // 预处理,删除重复点
    sort(p.begin(), p.end());
    p.erase(unique(p.begin(), p.end()), p.end());


    int n = p.size();
    int m = 0;
    vector<Point> ch(n+1);
    for(int i = 0; i < n; i++)
    {
        while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
        ch[m++] = p[i];
    }
    int k = m;
    for(int i = n-2; i >= 0; i--)
    {
        while(m > k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
        ch[m++] = p[i];
    }
    if(n > 1) m--;
    ch.resize(m);
    return ch;
}




inline double dist(Point a, Point b)
{
    return Length(a - b);
}

/************圆************/
struct Circle
{
    Point c;
    double r;
    Circle() {}
    Circle(Point c, double r):c(c), r(r) {}
    inline Point point(double a) //根据圆心角求点坐标
    {
        return Point(c.x+cos(a)*r, c.y+sin(a)*r);
    }
    inline void read()
    {
        scanf("%lf%lf%lf", &c.x, &c.y, &r);
    }
};

//求a点到b点(逆时针)在的圆上的圆弧长度
double DisOnCircle(Point a, Point b, Circle C)
{
    double ang1 = angle(a - C.c);
    double ang2 = angle(b - C.c);
    if (ang2 < ang1) ang2 += 2 * PI;
    return C.r * (ang2 - ang1);
}

// 过点p到圆C的切点
int getTangentPoints(Point p, Circle C, vector<Point>& v)
{
    Vector u = C.c - p;
    double dist = Length(u);
    if(dist < C.r) return 0;
    else if(dcmp(dist - C.r) == 0)   // p在圆上,只有一条切线
    {
        v.push_back(p);
        return 1;
    }
    else
    {
        double ang = asin(C.r / dist);
        double d = sqrt(dist * dist - C.r * C.r);
        v.push_back(p + Unit(Rotate(u, -ang)) * d);
        v.push_back(p + Unit(Rotate(u, +ang)) * d);
        return 2;
    }
}

//圆A与圆B的切点
void getTangentPoints(Circle A, Circle B, vector<Point>& a)
{
    if (A.r < B.r) swap(A, B);
    ///****************************
    int d2 = sqr(A.c.x - B.c.x) + sqr(A.c.y - B.c.y);
    int rdiff = A.r - B.r, rsum = A.r + B.r;
    if (d2 < rdiff * rdiff) return;   //内含

    ///***************************************
    double base = atan2(B.c.y - A.c.y, B.c.x - A.c.x);
    if (d2 == 0 && A.r == B.r) return;    //无线多条切线
    if (d2 == rdiff * rdiff)    //内切, 1条切线
    {
        ///**********************
        a.push_back(A.point(base));
        a.push_back(B.point(base));
        return;
    }
    //有外公切线
    double ang = acos((A.r - B.r) / sqrt(d2 * 1.0));
    a.push_back(A.point(base + ang)); a.push_back(B.point(base + ang));
    a.push_back(A.point(base - ang)); a.push_back(B.point(base - ang));
    if (d2 == rsum * rsum)  //一条内公切线
    {
        a.push_back(A.point(base));
        a.push_back(B.point(PI + base));
    }
    else if (d2 > rsum * rsum)  //两条内公切线
    {
        double ang = acos((A.r + B.r) / sqrt(d2 * 1.0));
        a.push_back(A.point(base + ang));
        a.push_back(B.point(PI + base + ang));
        a.push_back(A.point(base - ang));
        a.push_back(B.point(PI + base - ang));
    }
}



inline bool OnCircle(Point x, Circle c)
{
    return dcmp(c.r - Length(c.c - x)) == 0;
}


Circle C[60];
Point P[160];
vector<Point> ch, sol;
double t1, t2;
int main()
{
    int a, b;
    while (~RII(a, b))
    {
        ch.clear(), sol.clear();

        REP(i, a) C[i].read();
        b *= 3;
        REP(i, b)
        {
            P[i].read();
            sol.push_back(P[i]);
        }
        REP(i, b) REP(j, a)
        {
            getTangentPoints(P[i], C[j], sol);
        }
        FF(i, 0, a) FF(j, i + 1, a)
        {
            getTangentPoints(C[i], C[j], sol);
        }
        ch = ConvexHull(sol);
        double ans = 0;
        int len = ch.size();
        REP(i, len)
        {
            REP(j, a)
            {
                if (OnCircle(ch[i], C[j]) && OnCircle(ch[(i + 1) % len], C[j]))
                {
                    ans += DisOnCircle(ch[i], ch[(i + 1) % len], C[j]);
                    goto end;
                }
            }
            ans += dist(ch[i], ch[(i + 1) % len]);
            end:;
        }
        if (a == 1 && b == 0)
            ans += 2 * PI * C[0].r;
        printf("%.5lf\n", ans);
    }
    return 0;
}



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值