题意: 给出一系列点,这些点依次相连,最后一个点和第一个点相连,形成一个多边形。问以每个点为圆心画一个圆(半径可为0),是否存在当前的圆与相邻的两个圆都相外切,如果有,则找到使总面积最小的方案,如果没有则输出“IMPOSSIBLE”。
题解 : 我们设圆的半径为r[1] 到 r[n], 设多边形的边长为d[1] 到 d[n]。于是就有 r[1] + r[2] = d[1]
, r[2] + r[3] = d[2]
,
r[3] + r[4] = d[3]
, r[n-1] + r[n] = d[n-1]
, r[n] + r[1] = d[n]
。
到这里我们可以发现当n为奇数的情况和n为偶数的情况是不一样的。
①. n为奇数,这里我们以n = 3
为例, 那就有r[1] + r[2] = d[1]
, r[2] + r[3] = d[2]
,r[3] + r[1] = d[3]
, 容易化简得出 r[1] = (d[1] - d[2] + d[3]) / 2
。r[1]如果求出来了,那么剩下的所有r都很容易可以得到,那么面积就是固定的。不过这里需要注意如果出现r < 0
的情况,那么就输出“IMPOSSIBLE”。
②. n为偶数,这里我们以n = 4为例,那么就有r[1] + r[2] = d[1]
, r[2] + r[3] = d[2]
, r[3] + r[4] = d[3]
, r[4] + r[1] = d[4]
, 满足 d[1] + d[3] = d[2] + d[4]
, 也就是说只有在这种情况下才有可能有答案。
r[1] = r[1]
r[2] = d[1] - r[1]
r[3] = d[2] - d[1] + r[1]
r[4] = d[3] - d[2] + d[1] - r[1]
我们可以通过这些公式来求出r[1]的范围,因为r是非负数,所以
r[1] >= 0
, d[1] - r[1] >= 0
, d[2] - d[1] + r[1] >= 0
,
d[3] - d[2] + d[1] - r[1]>= 0
容易得到r[1]的范围,如果r[1]的左端点大于右端点,那么也输出"IMPOSSIBLE",我们已知 r[1] + r[2], r[2] + r[3] , r[3] + r[4] ...r[n-1] + r[n]
,要求min(r[1]^2 + r[2]^2 + r[3]^2 + r[4]^2 +...+ r[n]^2
, 其他的r全都可以转化成r[1],这就变成了一个关于r[1]的二次函数,所以就可以用三分r[1]的方法来找到最小的面积。
// #include<bits/stdc++.h>
#include<cstdio>
#include<vector>
#include<map>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<iomanip>
using namespace std;
const double pi = acos(-1.0);
#define inf 0x3f3f3f3f
#define ll long long
#define eps 1e-10
const int maxn = 10010;
const int mod = 1e9 + 7;
#define endl "\n"
int moven2[10][5] = {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}};
int moven1[10][5] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int t, n;
double d[maxn], r[maxn];
int sgn(double x){
if(fabs(x) < eps) return 0;
else return x < 0?-1:1;
}
struct Point{
double x,y;
Point(){}
Point(double x,double y):x(x),y(y){}
Point operator + (Point B){return Point(x + B.x,y + B.y);}
Point operator - (Point B){return Point(x - B.x,y - B.y);}
}P[maxn];
double Dist(Point A,Point B){
return sqrt((A.x - B.x)*(A.x - B.x) + (A.y - B.y)*(A.y - B.y));
}
double f(double radius){
double Sum = radius * radius;
r[1] = radius;
for(int i = 1; i < n; i++){
Sum += (d[i] - r[i]) * (d[i] - r[i]);
r[i + 1] = d[i] - r[i];
}
return Sum * pi;
}
double sanfen(double L, double R){
double midl, midr;
while(R - L > eps){
midl = L + (R - L)/ 3.0;
midr = R - (R - L)/ 3.0;
if(f(midl) > f(midr)) L = midl;
else R = midr;
}
return f(R);
}
int main() {
// ios::sync_with_stdio(false);
// cin.tie(0);cout.tie(0);
scanf("%d", &t);
while(t--){
memset(r, 0, sizeof(r));
memset(d, 0, sizeof(d));
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%lf%lf", &P[i].x, &P[i].y);
if(i >= 2){
d[i - 1] = Dist(P[i - 1], P[i]);
}
}
d[n] = Dist(P[1], P[n]);
if(n % 2){
bool f = 1;
double sum1 = 0;
for(int i = 1; i < n; i++){
if(i % 2) sum1 = sum1 + d[i];
else sum1 = sum1 - d[i];
}
r[1] = (sum1 + d[n]) / 2.0;
for(int i = 1; i < n; i++){
r[i + 1] = d[i] - r[i];
}
for(int i = 1; i <= n; i++){
if(sgn(r[i]) < 0){
f = 0;
break;
}
}
if(f == 0){
puts("IMPOSSIBLE");
continue;
}
double sum = 0;
for(int i = 1; i <= n; i++) sum += r[i] * r[i];
printf("%.2f\n", sum * pi);
for(int i = 1; i <= n; i++) printf("%.2f\n", r[i]);
}
else{
double left = 0, right = d[1], pre = d[1];
for(int i = 2; i <= n; i++){
pre = d[i] - pre;
if(i % 2) right = min(right, pre);
else left = max(left, -pre);
}
double ans = sanfen(left, right);
if(fabs(r[1] + r[n]) - d[n] > eps || left > right) puts("IMPOSSIBLE");
else{
printf("%.2f\n",ans);
for(int i = 1; i <= n; i++)
printf("%.2f\n",r[i]);
}
}
}
return 0;
}