• HDU 5531 Rebuild (2015长春现场赛,计算几何+三分法)


    Rebuild

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
    Total Submission(s): 521    Accepted Submission(s): 125


    Problem Description
    Archaeologists find ruins of Ancient ACM Civilization, and they want to rebuild it.

    The ruins form a closed path on an x-y plane, which has n endpoints. The endpoints locate on (x1,y1)(x2,y2),(xn,yn) respectively. Endpoint i and endpointi1 are adjacent for 1<in, also endpoint 1 and endpoint n are adjacent. Distances between any two adjacent endpoints are positive integers.

    To rebuild, they need to build one cylindrical pillar at each endpoint, the radius of the pillar of endpoint i is ri. All the pillars perpendicular to the x-y plane, and the corresponding endpoint is on the centerline of it. We call two pillars are adjacent if and only if two corresponding endpoints are adjacent. For any two adjacent pillars, one must be tangent externally to another, otherwise it will violate the aesthetics of Ancient ACM Civilization. If two pillars are not adjacent, then there are no constraints, even if they overlap each other.

    Note that ri must not be less than 0 since we cannot build a pillar with negative radius and pillars with zero radius are acceptable since those kind of pillars still exist in their neighbors.

    You are given the coordinates of n endpoints. Your task is to find r1,r2,,rn which makes sum of base area of all pillars as minimum as possible.



    For example, if the endpoints are at (0,0)(11,0)(27,12)(5,12), we can choose (r1r2r3r4)=(3.757.2512.759.25). The sum of base area equals to 3.752π+7.252π+12.752π+9.252π=988.816. Note that we count the area of the overlapping parts multiple times.

    If there are several possible to produce the minimum sum of base area, you may output any of them.
     
    Input
    The first line contains an integer t indicating the total number of test cases. The following lines describe a test case.

    The first line of each case contains one positive integer n, the size of the closed path. Next n lines, each line consists of two integers (xi,yi) indicate the coordinate of the i-th endpoint.

    1t100
    3n104
    |xi|,|yi|104
    Distances between any two adjacent endpoints are positive integers.
     
    Output
    If such answer doesn't exist, then print on a single line "IMPOSSIBLE" (without the quotes). Otherwise, in the first line print the minimum sum of base area, and then print n lines, the i-th of them should contain a number ri, rounded to 2 digits after the decimal point.

    If there are several possible ways to produce the minimum sum of base area, you may output any of them.
     
    Sample Input
    3 4 0 0 11 0 27 12 5 12 5 0 0 7 0 7 3 3 6 0 6 5 0 0 1 0 6 12 3 16 0 12
     
    Sample Output
    988.82 3.75 7.25 12.75 9.25 157.08 6.00 1.00 2.00 3.00 0.00 IMPOSSIBLE
     
    Source
     
     
     

    【题意】:

    给出一个N边形的顶点和边长,对于每个顶点作一圆,要求邻接顶点对应的两圆外切(不相邻顶点无要求)。找出一组Ri(R>=0)使得所有圆的面积和最小。

    【解题思路】:

    由于邻接点对应的圆要外切,则确定一个圆的半径r后,即可唯一确定所有圆的半径(Ri = di - Ri-1);设连接点 1 n 的边为 d1,以此类推。则必须满足 R1 + Rn = d1。根据此关系可以得到一个关于rd的等式。

    化简容易看出:

    ①当 n 为奇数时,可行的 r 可以唯一确定,r = d1+ d2+…+d(2n)) - (d3+…+d(2n+1));

    ②当 n 为偶数时,必定满足 d1+d3+d5+… = d2+d4+d6+…,否则IMPOSSIBLE(仅为必要条件)

      此时 S = r1^2 + … +rn^2  = ax^2+bx+c 为一个二次函数表达式。

      用 r 来表示Ri,首先可计算出a b c. 再根据每 ri <= di 这个关系求出r的取值范围[low, high].(无范围则IMPOSSIBLE

      有了二次函数表达式和自变量范围,用三分法求出半径平方和取最小值时的 r .

    根据①②求出的 r ,计算出所有的Ri,再依次判断每个Ri是否非负。

     

    注意精度问题(坑点!):

    由于本题只保留2位小数,计算面积和时,若用 S = Sumpi*r*r)计算则会出现精度问题(具体解释不清、亲测会挂),须在括号外乘PI,即 S = pi * Sumr*r.

     

    现场这题过得不多,封榜时一口气打完交了2WA,当时感觉思路非常清晰正确,不知道从哪调起,不停换机位到最后也没过。

    当时怨念很深,把代码打印带了回来查个究竟。先是在某论坛看到有人说精度这个坑,后来HDU重现赛再打,发现一个重大且傻逼的错误,当时求出二次函数后,以为最低点(-b/2a)如果取不到则IMPOSSIBLE当时想了三分以为跟这个是一样的),但显然要根据 r 的取值范围用三分法来逼近最小值。

     

    长春打铁很遗憾,第一次参加 ICPC 区域赛,既有遗憾也有教训。归根结底还是自己太弱,而且还不好好训练。明年就大三了,有了这次教训,接下来一年一定要好好训练好好总结,争取早日取得成绩。同时,stark 目前也还有各方面欠缺,希望能跟队友共勉,一起努力。

    Upd 15.11.6 合肥赛前总结下长春得失,希望合肥站加油!(默默感谢队友wqp让出这个机会).

     
     
     
      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<cmath>
      5 #include<algorithm>
      6 #define eps 1e-8
      7 #define PI acos(-1.0)
      8 #define LL long long
      9 #define maxn 100100
     10 #define IN freopen("in.txt","r",stdin);
     11 using namespace std;
     12 
     13 
     14 struct Point {
     15     double x,y;
     16 };
     17 
     18 int sign(double x) {
     19     if(fabs(x)<eps) return 0;
     20     return x<0? -1:1;
     21 }
     22 
     23 double Distance(Point p1,Point p2) {
     24     return sqrt((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y));
     25 }
     26 
     27 
     28 int n;
     29 Point p[maxn];
     30 double d[maxn];
     31 
     32 int check(double r)
     33 {
     34     if(sign(r) < 0) return 0;
     35 
     36     for(int i=2 ;i<=n; i++) {
     37         r = d[i]-r;
     38         if(sign(r) < 0) return 0;
     39     }
     40     return 1;
     41 }
     42 
     43 
     44 /*三分法求面积最小时的r*/
     45 double a,b,c;
     46 double cal(double x) {
     47     return a*x*x + b*x + c;
     48 }
     49 
     50 double solve(double low, double high)
     51 {
     52     while(high-low > eps){
     53         double mid = (high+low)/2.0;
     54         double midmid = (mid+high)/2.0;
     55         if(sign(cal(midmid)-cal(mid)) > 0) high = midmid;
     56         else low = mid;
     57     }
     58 
     59     return low;
     60 }
     61 
     62 
     63 int main(int argc, char const *argv[])
     64 {
     65     //IN;
     66 
     67     int t;scanf("%d",&t);
     68     while(t--)
     69     {
     70         double r = 0;
     71         memset(p, 0, sizeof(p));
     72         memset(d, 0, sizeof(d));
     73         int flag = 1;
     74 
     75         scanf("%d", &n);
     76         for(int i=1 ;i<=n; i++)
     77             scanf("%lf %lf", &p[i].x, &p[i].y);
     78         d[1]=Distance(p[1], p[n]);
     79         for(int i=2; i<=n; i++)
     80             d[i] = Distance(p[i], p[i-1]);
     81 
     82         if(n%2 != 0){
     83             r = d[1];
     84             for(int i=2; i<=n; i++){
     85                 if(i%2 == 0) r += d[i];
     86                 else r -= d[i];
     87             }
     88             r /= 2.0;
     89             flag = check(r);
     90         }
     91 
     92         else{
     93             double tmp = 0;
     94             for(int i=1; i<=n; i++){
     95                 if(i%2 != 0) tmp += d[i];
     96                 else tmp -= d[i];
     97             }
     98             if(sign(tmp) != 0) {flag=0; goto end;}
     99 
    100             /*半径平方和的二次表达式*/
    101             a=1.0; b=0; c=0;
    102             tmp = 0;
    103             for(int i=2; i<=n; i++){
    104                 a += 1.0;
    105                 tmp = d[i]-tmp;
    106                 c += tmp*tmp;
    107                 if(i%2 == 0) b -= 2.0*tmp;
    108                 else b += 2.0*tmp;
    109             }
    110 
    111             /*
    112                 错误地以为最低点为唯一取值点,若最低点不符合则无解.
    113                 r=(-b)/(2.0*a);
    114                 if(sign(r)<0) r=0;
    115                 flag = check(r);
    116             */
    117 
    118             /*检查每条边计算三分时r的可行范围*/
    119             double low=0, high = d[1];
    120             tmp = 0;
    121             for(int i=2; i<=n; i++){
    122                 if(i%2 == 0){
    123                     tmp += d[i];
    124                     high = min(high, tmp);
    125                 }
    126                 else{
    127                     tmp -= d[i];
    128                     low = max(low, tmp);
    129                 }
    130             }
    131 
    132             if(low > high) flag = 0;
    133             else{
    134                 r = solve(low, high);
    135                 flag = check(r);
    136             }
    137         }
    138 
    139         end:
    140         if(!flag) printf("IMPOSSIBLE
    ");
    141         else{
    142             double R[maxn];
    143             R[1]=r;
    144             double temp = 0;
    145             for(int i=2; i<=n; i++){
    146                 temp = d[i]-R[i-1];
    147                 R[i] = temp;
    148             }
    149             double ans = 0;
    150             for(int i=1; i<=n; i++){
    151                 //ans += R[i]*R[i]*PI;精度问题
    152                 ans += R[i]*R[i];
    153             }
    154 
    155             printf("%.2lf
    ", ans*PI);
    156             for(int i=1; i<=n; i++){
    157                 printf("%.2lf
    ", R[i]);
    158             }
    159         }
    160 
    161     }
    162 
    163     return 0;
    164 }
  • 相关阅读:
    nginx部署vue工程和反向代理nodejs工程
    memcache 原理 & 监测 & 查看状态 & stats & 结构
    CRT(secureCRT)中文显示研究&Linux中文字符显示
    Linux rz的使用
    htmlspecialschars与htmlentities的区别
    ie下php session不能用(域名的合法定义)
    常用生产环境的PHP安装参数
    XMLREADER/DOM/SIMPLEXML 解析大文件
    Linux中的小括号和大括号,${}/$()/()/{}/${var:-string}/${var:=string}/${var:+string}/${var:?string}/${var%pattern}/${var#pattern}/${var%%pattern}/${var##pattern}
    好用的window命令
  • 原文地址:https://www.cnblogs.com/Sunshine-tcf/p/4940699.html
Copyright © 2020-2023  润新知