HDU 4667 Building Fence

 题意: 平面里给出M个三角形, N个圆形, 图形之间两两不相交, 求一个把这些图形围起来周长最小的一个圈的周长~
分析:把三角形顶点分解成点, 对圆形求可能的切点:1.点和圆的两个切点, 2.圆和圆的外公切线切点。  
             然后对所有点求凸包, 处理周长的时候, 如果凸包上两个相邻点在同一个圆上, 则求相应的弧长~。此方法有个trick: 只有一个圆的时候需要特殊处理.
深跪不起:1.对凸包上点取共线点的时候一直wa, 求解释~, 2. 避免直接三角函数, 用精度比较高的方法样例不过~

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>

#define FOR(i, n) for(int i = 0; i < n; i++)
#define MEM(array) memset(array, 0, sizeof(array))
#define eps 1e-10
#define INF (int)1e7
#define PI acos(-1.0)
#define MAX (int)222
using namespace std;
inline int sgn(const double &x){return x > eps ? 1 : (x < -eps ? -1 : 0);}
inline double sqr(const double &x){return x * x;}
struct Point {
    double x, y;
    Point() {}
    Point(const double &xx, const double &yy):x(xx), y(yy) {}
    friend Point operator + (const Point &b, const Point &a) {
        return Point(b.x + a.x, b.y + a.y);
    }
    friend Point operator - (const Point &b, const Point &a) {
        return Point(b.x - a.x, b.y - a.y);
    }
    Point operator * (const double &a)const{
        return Point(x * a, y * a);
    }
    Point operator / (const double &a) const{
        return Point(x / a, y / a);
    }
    friend double dot(const Point &a, const Point &b) {
        return a.x *b.x + a.y * b.y;
    }
    friend double det(const Point &a, const Point &b) {
        return a.x * b.y - a.y * b.x;
    }
    double len( )const {
        return sqrt(dot(*this, *this));
    }
    bool operator < (const Point &a)const {
        return sgn(x - a.x) < 0 || (sgn(x - a.x) == 0 && sgn(y - a.y) < 0);
    }
    bool operator == (const Point &a)const {
        return sgn(x - a.x ) == 0 && sgn(y - a.y) == 0;
    }
    Point rotate(double angle){
        double x1 = x * cos(angle) - y * sin(angle);
        double y1 = x * sin(angle)  + y * cos(angle);
        return Point(x1, y1);
    }
    Point rotate2(const double &cosa,const double &sina){
        double x1 = x * cosa - y * sina;
        double y1 = x * sina + y * cosa;
        return Point(x1, y1);
    }
    Point norm()const{
        return Point(x / len(), y / len());
    }
    void in(){
        scanf("%lf %lf", &x, &y);
    }
    void out()const {
        printf("%.4f    %.4f\n", x, y);
    }
};
int n, m;
struct CirPoint{
    Point p;
    int id;//属于某个圆形
    void in(){ p.in(); }
    bool operator < (const CirPoint &a)const {
        return p < a.p;
    }
}cp[MAX * MAX], tubao[MAX *  MAX];
int ncp, ntb;
struct Cir{
    Point p;
    double r;
    friend vector<Point> pointQieCir(const Point &p, const Cir &cir){
        Point va = (p - cir.p).norm() * cir.r;
        double angle = acos(cir.r / (p - cir.p).len());
//        double cosa = cir.r / (p - cir.p).len();
//        double sina = sqrt(1 + sqr(cosa));
        vector<Point>res;
//        res.push_back( cir.p + va.rotate2(cosa, sina));
//        res.push_back( cir.p + va.rotate2(cosa, -sina));
        res.push_back( cir.p + va.rotate(angle));
        res.push_back( cir.p + va.rotate(-angle));
        return res;
    }
    friend void cirWgCir(const Cir &cir1, const int &id1, const Cir &cir2, const int &id2){
        Point vn = (cir2.p - cir1.p).norm();
        double angle = acos( (cir1.r - cir2.r)/(cir1.p -cir2.p).len() );
//        double cosa = cir1.r - cir2.r/(cir1.p -cir2.p).len(), sina = sqrt(1 - sqr(cosa));
        Point  vc1 = vn * cir1.r, vc2 = vn * cir2.r;
//        Point a = cir1.p + vc1.rotate2(cosa, sina), b = cir1.p + vc1.rotate2(cosa, -sina);
        Point a = cir1.p + vc1.rotate(angle), b = cir1.p + vc1.rotate(-angle);
        cp[ncp].p = a; cp[ncp++].id = id1;
        cp[ncp].p = b; cp[ncp++].id = id1;

//        a = cir2.p + vc2.rotate2(cosa, sina); b = cir2.p + vc2.rotate2(cosa, -sina);
        a = cir2.p + vc2.rotate(angle); b = cir2.p + vc2.rotate(-angle);
        cp[ncp].p = a; cp[ncp++].id = id2;
        cp[ncp].p = b; cp[ncp++].id = id2;
    }
    void in(){
        p.in();
        scanf("%lf", &r);
    }
}cir[MAX];
void input(){
    for(int i = 0; i < n; i++){
        cir[i].in();
    }
    ncp = 0;
    for(int i =0; i < m; i++){
        for(int j = 0; j < 3; j++){
            cp[ncp].in();
            cp[ncp++].id = -1;//三角形顶点
        }
    }
}
void getCirPoint(){
    //point to cir
    int tn = ncp;
    vector<Point>res;
    for(int i = 0; i < tn; i++){
        for(int j = 0; j < n; j++){
            res = pointQieCir(cp[i].p, cir[j]);
            cp[ncp].p = res[0]; cp[ncp++].id = j;
            cp[ncp].p = res[1]; cp[ncp++].id = j;
        }
    }
    //cir to cir
    for(int i = 0; i < n; i++){
        for(int j = i + 1; j < n; j++){
            cirWgCir(cir[i], i, cir[j], j);
        }
    }
}

void hull(){
    ntb = 0;
    sort(cp, cp + ncp);
    for(int i = 0; i < ncp; i++){
        while(ntb > 1 && sgn(det(tubao[ntb - 1].p - tubao[ntb - 2].p,  cp[i].p - tubao[ntb - 2].p)) <= 0)ntb--;
        tubao[ntb++] = cp[i];
    }
    int k = ntb;
    for(int i = ncp - 2; i >= 0; i--){
        while(ntb > k && sgn(det(tubao[ntb -1].p - tubao[ntb - 2].p, cp[i].p - tubao[ntb - 2].p)) <= 0)ntb--;
        tubao[ntb++] = cp[i];
    }
}
void getLen(){
    double ans = 0;
    for(int i  = 0 ; i < ntb - 1; i++){
        if(tubao[i].id != -1 && (tubao[i].id == tubao[(i + 1) ].id)){
            Point va = tubao[i].p - cir[tubao[i].id].p;
            Point vb = tubao[(i + 1)].p - cir[tubao[i].id].p;
            double angle = acos(dot(va, vb)/ va.len() / vb.len());
            int res = sgn(det(va, vb));
            if(res < 0)angle = 2 * PI - angle;
            double t = cir[tubao[i].id].r * angle;
            ans += t;
        }else {
            double t = (tubao[i].p - tubao[(i + 1)].p).len();
            ans += t;
        }
    }
    printf("%.10f\n",ans);
}
int main()
{
    while(cin>>n>>m){
        input();
        if(m == 0 && n == 1){//trick
            printf("%.10f\n", 2 * PI * cir[0].r);
            continue;
        }
        getCirPoint();
        hull();
        getLen();
    }
    return 0;
}
/*
*/



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值