HDU 5531 Rebuild(三分)——2015ACM/ICPC亚洲区长春站

传送门

Rebuild

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


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 endpoint i1 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 ( r1 , r2 , r3 , r4 ) = (3.75, 7.25 , 12.75 , 9.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

题目大意:

n 个点,其中这 n 个点能够组成一个凸 n 边形,而且是按照顺序的,这样就不用排序了,如果不给定顺序的话,应该按照极角进行排序,现在以每个点为圆心,保证两个相邻点为圆心的圆是相切的,现在求的是所有的圆的面积和,并且将每个圆的半径输出。

解题思路:

因为保证相邻圆是相切的,所以我们设第一个圆的半径为 r1 ,那么有如下式子成立:
r1+r2=d1
r2+r3=d2
       ...  
rn+r1=dn

然后我们来解这个方程,解的过程中就会发现有一个小规律,跟 n 的奇偶有关系,即:当 n 为奇数的时候,就会有唯一解能够解出 r1 r1=d1d2+d3+...+dn2
如果是偶数的话,我们发现解不出这个方程,那么怎么做的,因为求的是面积,所以最终的结果一定是关于 r1 的二次方程,又因为,二次函数我们可以通过 三分 来求得区间中的极值,我认为这个题的关键就是如何确定 r1 的三分范围,还是来考虑上述的方程,我们将其变形为:
r10
d1r10
d2d1+r10
       ...  

然后我们就能求得一个区间 [l,r] , 因为这个区间中的数都是符合的,所以才进行三分。
还有一个小 trick 就是,偶数的时候我们必须求得验证一个条件:奇数边的距离是等于偶数边的距离的。

代码:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <algorithm>
using namespace std;
const int MAXN = 1e4+5;
const double PI = acos(-1.0);
const double eps = 1e-8;
struct Point{
    double x, y;
}p[MAXN];
double Dis(Point a, Point b){
    return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
double dis[MAXN], r[MAXN];
int n;
double f(double x)
{
    r[0] = x;
    double ans = r[0]*r[0];
    for(int i=1; i<n; i++){
        r[i] = dis[i-1]-r[i-1];
        ans += r[i]*r[i];
    }
    return ans;
}
double sanfen(double left, double right)
{
    double midl, midr;
    while (right-left > eps)
    {
        midl = (left + right) / 2;
        midr = (midl + right) / 2;
        if(f(midl) <= f(midr)) right = midr;
        else left = midl;
    }
    return left;
}

int main()
{
    int T; scanf("%d", &T);
    while(T--){
        scanf("%d", &n);
        for(int i=0; i<n; i++) scanf("%lf%lf", &p[i].x, &p[i].y);
        for(int i=0; i<n; i++) dis[i] = Dis(p[i], p[(i+1)%n]);
        r[0] = 0;
        for(int i=0; i<n; i++){
            if(i & 1) r[0] -= dis[i];
            else  r[0] += dis[i];
        }
        if(n & 1){
            r[0] /= 2;
            double ans = PI*r[0]*r[0];
            int ok = 0;
            if(r[0]>-eps && r[0]<=dis[0]){
                for(int i=1; i<n; i++){
                    r[i] = dis[i-1]-r[i-1];
                    if(r[i]<-eps || r[i]>dis[i]){
                        ok = 1;
                        break;
                    }
                    ans += PI*r[i]*r[i];
                }
            }
            else ok = 1;
            if(ok) puts("IMPOSSIBLE");
            else{
                printf("%.2f\n",ans);
                for(int i=0; i<n; i++) printf("%.2f\n",r[i]);
            }
        }
        else{
            double l = 0, rr = 1e9;
            if(fabs(r[0]) > eps) { puts("IMPOSSIBLE"); continue; }
            double tmp = 0;
            for(int i=1; i<n; i++){
                tmp = dis[i-1] - tmp;
                if(i & 1) rr = min(rr, tmp);
                else l = max(l, -tmp);
            }
            double ans = sanfen(l, rr);
            if(l > rr) puts("IMPOSSIBLE");
            else{
                printf("%.2f\n",f(ans)*PI);
                for(int i=0; i<n; i++) printf("%.2f\n", r[i]);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值