1. 程式人生 > >HDU - 5531- E - Rebuild(幾何)

HDU - 5531- E - Rebuild(幾何)

連結:

http://acm.hdu.edu.cn/showproblem.php?pid=5531

題意:

順序給出二維座標系中的點,讓你找出以每個點為圓心的圓,使得相鄰的點對應的圓相切,問圓的最小面積之和是多少.

思路:

這題其實比賽時就看出來了點端倪,看了題解加深了認識.

這題有以下特點:

1.確定第一個點的半徑,則其餘的半徑都能確定.

2.第一個點半徑改變,偶數點的變化與其相反,奇數點與之相同.

3.半徑必須大於等於0(這個是用來判斷半徑是否合理的).

4.奇數個點時,如果存在答案,則答案唯一.

   

#include <bits/stdc++.h>
using namespace std;
const double pi = acos(-1);
const double eps = 1e-10;
const int maxn = 1e5 +10;
double len[maxn],f[maxn],x;
int n;
struct Point
{
    double x,y;
} point[maxn];
double length(Point a,Point b)
{
    return sqrt((a.x-b.x)*(a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
void init()
{
    for(int i = 0 ; i < n-1; i++)
    {
        len[i] = length(point[i],point[i+1]);
    }
    len[n-1] = length(point[0],point[n-1]);
}
void print(double ans)
{
    printf("%.2f\n",ans * pi);
    for(int i = 0; i < n ; i++)
    {
        if(i & 1)
            printf("%.2f\n",f[i]-x);
        else
            printf("%.2f\n",f[i]+x);
    }
    return ;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        double maxx = 0x3f3f3f3f, minn =0;
        scanf("%d",&n);
        for(int i = 0; i < n; i++)
            scanf("%lf%lf",&point[i].x,&point[i].y);
        init();
        f[0] = 0;
        //先假設半徑 r - 0 求出相應的半徑
        //注意,此時求出的半徑,都是極限半徑,偶數點對應極限大,奇數點對應極限小
        //據此判斷出半徑的範圍
        for(int i = 1; i < n; i++)
        {
            f[i] = len[i-1] - f[i-1];
            if((i&1) && f[i] < maxx)
            {
                maxx = f[i];
            }
            if(!(i & 1) &&(-f[i]) > minn)
            {
                minn = -f[i];
            }
        }
        if(minn >= maxn + eps )
        {
            printf("IMPOSSIBLE\n");
            continue;
        }
        if(n & 1)
        {
            x = 1.0*(len[n-1] - f[n-1]) / 2.0; //根據最後一個半徑的長度確定的
            if(x <= minn - eps || x >= maxx + eps)
            {
                printf("IMPOSSIBLE\n");
                continue;
            }
            double ans = 0.0;
            for(int i = 0; i < n ; i++)
            {
                if(i & 1)
                {
                    ans += (f[i] - x) * (f[i] - x);
                }
                else
                {
                    ans += (f[i] + x) * (f[i] + x);
                }
            }
            print(ans);
        }
        else
        {
            if( fabs(f[n-1]-len[n-1]) > eps || (minn - maxx) > eps)
            {
                printf("IMPOSSIBLE\n");
                continue;
            }
            double a = 0,b = 0, c = 0;
            for(int i = 0; i < n; i++)//偶數情況,構成二次函式,判斷對稱軸和半徑取值範圍即可
            {
                a = a + 1;
                c += f[i] * f[i];
                if(i & 1)
                {
                    b -= 2 * f[i];
                }
                else
                {
                    b += 2 * f[i];
                }
            }
            double l = (-b / 2) / a;
            if( l < minn + eps)
            {
                x = minn;
            }
            else if( maxx < l + eps)
            {
                x = maxx;
            }
            else x = l;
            print(a * x * x + b * x + c);
        }
    }
    return 0;
}