2008年百度之星程序设计大赛初赛第2场 第4题 圆面覆盖 解法

/*题目描述:-----------------------------------------------------------------------------------------------*/
圆面覆盖
在平面上有一个长为L,宽为W的长方形,左下角坐标为(0,0),右上角坐标为(L,W)。给定一些圆,第i个圆的圆心坐标为(xi,yi),半径为 Ri。
你的任务是求最小的正实数k,使得把每个圆的半径变为原来的k倍后(即:第i个圆半径变为kRi,圆心位置不变),长方形将被这些圆完全覆盖。换句话说,长方形内部或边界上的任意点均至少在一个圆的内部或边界上。
输入格式
输入第一行包含三个整数n,L,W(1<=n<=50,1<=L,W<=1000),即圆的个数、长方形的长和宽。
以下n行,每行三个不超过1000的正整数xi,yi和Ri。
输出格式
仅一行,包含一个实数k,保留小数点后三位。
样例输入
122
111
样例输出
1.414

/*算法描述与分析 -----------------------------------------------------------------------------------------------*/
积分(integral)+二分搜索。利用积分原理判断“左下点位(x1,y1)右上点位(x2,y2)的矩形”是否被圆面集合覆盖,
将该矩形分为许多小的矩形,验证每个矩形是否被被圆面集合覆盖。理论上,当矩形趋近无穷小时,既为答案;算法中,预定一个精确度pre,
当长、宽小于该精确度时停止计算。利用二分法搜索k。
算法的复杂度不好估计,最坏情况(1000/pre)*(1000/pre)*圆面个数*lg(1024)。
实际,在测试例子中,当pre为0.0001时,计算次数30810次,耗时0.016秒。

算法题编译环境和运行环境见“算法题编译环境和运行环境说明”

/*测试用例 -------------------------------------------------------------------------------------------------------*/
来源:http://tieba.baidu.com/f?kz=394652610
50 1000 1000
458 48 44
897 320 15
503 854 19
63 255 36
272 628 7
56 587 27
761 141 26
387 136 10
128 68 25
61 686 44
89 59 40
592 754 24
354 588 47
628 389 44
643 645 48
432 231 19
81 369 21
235 549 30
563 80 40
153 524 14
645 508 10
221 185 16
480 410 21
791 808 1
506 425 3
573 939 25
32 494 18
638 543 36
818 963 5
131 104 1
80 812 19
705 130 8
186 963 8
189 632 7
783 632 44
992 903 10
946 949 13
898 593 3
653 698 11
753 250 45
291 453 6
39 590 17
191 249 45
959 355 34
8 576 22
24 348 36
69 886 35
535 708 35
262 642 34
805 128 26


/*源代码 -------------------------------------------------------------------------------------------------------*/
#include   <iomanip>
#include "../edan_readfile.cpp"
using namespace std;

#define distance(x1,y1,x2,y2) sqrt(((x1)-(x2))*((x1)-(x2))+((y1)-(y2))*((y1)-(y2)))
#define max(x,y) (x)<(y)? (y): (x)
#define min(x,y) (x)<(y)? (x): (y)


#define precision 0.0001
#define pre 0.0001

static unsigned int cc=0;

class circle{
public:
    unsigned int x,y,r;
    circle(unsigned int xx,unsigned int yy,unsigned int rr):x(xx),y(yy),r(rr){}
};


class baiducircle{
    int W,L;
    vector<circle> circles;
    bool checkmulti(double left,double down,double right, double top, double x);
    double searchmulti();
public:
    int doit();
};

int baiducircle::doit(){
    //set value W,L,circle
    edanreadfile *fr=new edanreadfile();
    int x,y,r;
    vector<circle>::iterator it;
    fr->setfile("in.txt");
    fr->readint(x,' ');fr->readint(L,' ');fr->readint(W,' ');
    while(!fr->is_over()){
        if(!fr->readint(x,' ')) break;
        if(!fr->readint(y,' ')) break;
        if(!fr->readint(r,' ')) break;
        circles.push_back(circle(x,y,r));
    }
    delete fr;
    cout<<fixed<<setprecision(3)<<searchmulti();
    return 1;
}

bool baiducircle::checkmulti(double left,double down,double right, double top, double x){
    bool leftdown,lefttop,rightdown,righttop;
    bool leftdown_last,lefttop_last,rightdown_last,righttop_last;
    vector<circle>::iterator it;
    leftdown_last=lefttop_last=rightdown_last=righttop_last=false;
    for(it=circles.begin();it!=circles.end();it++){
        leftdown=(distance(left,down,it->x,it->y) < x*it->r);
        lefttop=(distance(left,top,it->x,it->y) < x*it->r);
        rightdown=(distance(right,down,it->x,it->y) < x*it->r);
        righttop=(distance(right,top,it->x,it->y) < x*it->r);
        leftdown_last=leftdown_last||leftdown;
        lefttop_last=lefttop_last||lefttop;
        rightdown_last=rightdown_last||rightdown;
        righttop_last=righttop_last||righttop;
        if(leftdown&&lefttop&&rightdown&&righttop) break;
        cc++;
    }
    if(it!=circles.end()) return true;
    if(!(leftdown_last&&lefttop_last&&rightdown_last&&righttop_last)) return false;
    if((right-left<pre)&&(top-down<pre)) return false;

    leftdown=checkmulti(left,down,(left+right)/2,(top+down)/2,x);
    if (!leftdown) return false;
    lefttop=checkmulti(left,(top+down)/2, (left+right)/2, top,x);
    if (!lefttop) return false;
    rightdown=checkmulti((left+right)/2,down,(top+down)/2,right,x);
    if (!rightdown) return false;
    righttop=checkmulti((left+right)/2,(top+down)/2,right,top,x);
    if (!righttop) return false;
    return true;

}

double baiducircle::searchmulti(){
    double small, big, now;
    double tx,tn;
    vector<circle>::iterator it;
    small=2000.0;big=2000.0;
    for(it=circles.begin();it!=circles.end();it++){
        tx=0.0;tn=2000.0;
        tx=max(tx,distance(0.0,0.0,it->x,it->y)/it->r);
        tx=max(tx,distance(0.0,W,it->x,it->y)/it->r);
        tx=max(tx,distance(L,0.0,it->x,it->y)/it->r);
        tx=max(tx,distance(L*1.0,W,it->x,it->y)/it->r);
        big=min(big,tx);
        tn=min(tn,distance(0.0,0.0,it->x,it->y)/it->r);
        tn=min(tn,distance(0.0,W,it->x,it->y)/it->r);
        tn=min(tn,distance(L,0.0,it->x,it->y)/it->r);
        tn=min(tn,distance(L*1.0,W,it->x,it->y)/it->r);
        small=min(small,tn);
    }
    while(big-small>precision){
        now=(big+small)/2.0;
        if(checkmulti(0,0,L,W,now)) big=now;
        else small=now;
    }
    cout<<"cc:"<<cc<<endl;
    return (big+small)/2.0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值