1. 程式人生 > >HDU - 5531(Rebuild )分析+三分

HDU - 5531(Rebuild )分析+三分

題意:

給定一個n,代表有n個點,給出n個座標,以每個座標為圓心畫圓。

要求:①相鄰的兩個點畫的圓必須相切(第i個和第i+1個,及第一個點和最後一個點)

           ②所有圓的面積之和最小。

注:半徑可以為0。

分析:

  1. 如果n為奇數,通過列方程可以知道,r1=(d1+d3+...-d2-d4-...)/2,可以發現r1是確定的。(不懂可以用n=3舉個栗子,把方程列出來就明白了)
    那麼就可以直接求r1,然後 r[i]=dist[i-1]-r[i-1] 依次把所有的r求出來,檢驗一下①是否有r是負數,②r[1]+r[n]是否等於dist[n];
  2. 如果n是偶數,列完方程後會發現,不能像n為奇數一樣把r1用d1、d2...表示出來,則說明r1是不確定的,那麼所有的r也不確定。(可以舉個n=4的梨子,和n=3比較比較)
    那麼答案我們無法直接求出,但可能存在一個最大值,用三分法求即可。

程式碼:

#include <set>
#include <map>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <iomanip>
#include <iostream>
#include <algorithm>
#define clr(str,x) memset(str,x,sizeof(str))
#define FRER() freopen("in.txt","r",stdin);
#define FREW() freopen("out.txt","w",stdout);
#define INF 0x3f3f3f3f
#define maxn 10100

typedef long long int ll;
using namespace std;
const double PI =acos(-1.0);
double r[maxn],dist[maxn];
int n;
struct node
{
    double x,y;
} nodes[maxn];
double getDist(node a,node b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double f(double r1)
{
    r[1]=r1;
    double sum=PI*r1*r1;
    for(int i=2; i<=n; i++)
    {
        r[i]=dist[i-1]-r[i-1];
        sum+=PI*r[i]*r[i];
    }
    return sum;
}
double Sanfen(double& l,double& r)
{
    int K=200;
    while(K--)
    {
        double mid=(l+r)/2;
        double mmid=(mid+r)/2;
        if(f(mid)<f(mmid))
            r=mmid;
        else
            l=mid;
    }
    return  f(l)<f(r) ? l:r;
}
int main()
{
    //FRER()
    //FREW()
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(r,0,sizeof(r));
        memset(dist,0,sizeof(dist));
        bool isyes=true;
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            scanf("%lf%lf",&nodes[i].x,&nodes[i].y);
            if(i==1)
                continue;
            dist[i-1]=getDist(nodes[i],nodes[i-1]);
        }
        dist[n]=getDist(nodes[n],nodes[1]);
        if(n&1)
        {
            double s0=0;
            for(int i=1; i<=n; i++)
            {
                if(i&1)
                    s0+=dist[i];
                else
                    s0-=dist[i];
            }
            r[1]=s0/2;
            for(int i=2; i<=n; i++)
            {
                r[i]=dist[i-1]-r[i-1];
            }
            if(r[1]+r[n]!=dist[n])
                isyes=0;
        }
        else
        {
            double t1=0,t2=0;
            for(int i=1; i<=n; i++)
            {
                if(i&1)  t1+=dist[i];
                else  t2+=dist[i];
            }
            if(t1-t2!=0)
            {
                isyes=0;
            }
            else
            {
                double s0=0;
                double l=0,r=min(dist[1],dist[n]);
                for(int i=1; i<=n; i++)
                {
                    if(i&1)
                    {
                        s0+=dist[i];
                        r=min(r,s0);
                    }
                    else
                    {
                        s0-=dist[i];
                        l=max(l,s0);
                    }
                }
                double r1;
                r1=Sanfen(l,r);
                f(r1);
            }
        }
        for(int i=1;i<=n;i++)
            if(r[i]<0)
                isyes=false;
        if(!isyes)
            printf("IMPOSSIBLE\n");
        else
        {
            double ans=0;
            for(int i=1; i<=n; i++)
            {
                ans+=r[i]*r[i];
            }
            ans*=PI;
            printf("%.2lf\n",ans);
            for(int i=1; i<=n; i++)
                printf("%.2lf\n",r[i]);
        }
    }
    return 0;
}