链接:
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;
}