题意是给你n个点,然后要求以每个点为圆心画一个圆,使得两两相邻的圆心的圆相切,不相邻的圆可以相交,如果可以作这样的圆那么输出圆面积和的最小值和,每个圆的半径,否则输出IMPOSSIBLE。
首先我们求出每一条邻边的长度,记为l1, l2, l3, ... ,ln, 假定第一个圆的半径为x,那么我们可以用x表示所有圆的半径,分别为x, l1-x, l2-l1+x, l3-l2+l1-x .... 需要利用的就是第一个半径和最后一个半径的和必须等于ln(因为相切)。 发现最后一项+x还是-x和n的奇偶性有关,很明显需要分类。
若n是奇数:
那么最后一个圆的半径是l[n-1]-l[n-2] + l[n-3]-l[n-4] + ... + l[2]-l[1] + x, 它和第一个圆的半径x的和必须是l[n], 所以可以求出x,因为x的值必须是这个,所以只需要简单的判定是不是所有的半径都大于等于0,如果不是就无解,否则计算结果。
若n是偶数:
那么最后一个圆的半径是l[n-1]-l[n-2] + l[n-3]-l[n-4] + ... + l[3]-l[2] + l[1] - x, 它和第一个圆的半径x的和必须是ln[n] ,可以发现是一个常数方程,要么恒成立要么恒不成立。恒不成立显然就是无解的,如果恒成立,答案就是x^2 + (l[1]-x)^2 + (l[2]-l[1]+x)^2 + ... + (l[n-1]-l[n-2] + l[n-3]-l[n-4] + ... + l[3]-l[2] + l[1] - x)^2的结果乘以pi,这个式子展开是关于x的二次函数,可以通过三分求出极值,但是我们需要确定x的范围。显然x只需要满足任意一个圆的半径都非负就可以了,如果x的范围为空就无解,否则x的最优解就是区间三分找到的极值。
#include <bits/stdc++.h> using namespace std; #define maxn 11111 #define eps 1e-7 #define pi acos (-1) struct point { double x, y; }p[maxn]; double l[maxn]; //存第i条边到i+1条边的距离 double g[maxn]; //存l[n-1]-l[n-2]+l[n-3]-l[n-4]... double ans[maxn]; //第i个圆的面积 int n; double A, B, C; //二次函数的三个参数Ax^2+Bx+C double dis (point a, point b) { double xx = a.x-b.x, yy = a.y-b.y; return sqrt (xx*xx + yy*yy); } double f (double x) { return A*x*x + B*x + C; } void work (double &L, double &R) { l[0] = l[n]; L = 0, R = min (l[0], l[1]); for (int i = 1; i <= n; i++) { if (i&1) { L = max (L, -1*g[i]); R = min (R, min (l[i-1], l[i])-g[i]); } else { L = max (L, g[i]-min (l[i-1], l[i])); R = min (R, g[i]); } } return ; } void solve_even () { //n是偶数 if (fabs (l[n]-g[n]) > eps) { //无解 printf ("IMPOSSIBLE\n"); return ; } A = n; B = C = 0.0; for (int i = 1; i <= n; i++) { int id = ((i&1)? 1 : -1); //-x还是+x B += 2.0*g[i]*id; C += g[i]*g[i]; } double L, R, LL, RR; work (L, R); //找到x的范围 if (L > R) { printf ("IMPOSSIBLE\n"); return ; } while (R-L > eps) { LL = (L*2+R)/3, RR = (L+2*R)/3; double p1 = f (LL), p2 = f (RR); if (p1 > p2) L = LL; else R = RR; } double x = (L+R)/2.0; //第一个半径为x的时候总面积最小 bool ok = 1; memset (ans, 0, sizeof ans); for (int i = 1; i <= n; i++) { int id = ((i&1)? 1 : -1); //+x还是-x double cur = g[i]+x*id; //当前半径 if (cur >= 0) { ans[i] = cur; } else { //出现了负数 ok = 0; break; } } if (!ok) { printf ("IMPOSSIBLE\n"); return ; } double sum = 0.0; for (int i = 1; i <= n; i++) { sum += ans[i]*ans[i]; } printf ("%.2f\n", sum*pi); for (int i = 1; i <= n; i++) printf ("%.2f\n", ans[i]); return ; } void solve_odd () { //n是奇数 double x = (l[n]-g[n]) / 2.0; bool ok = 1; memset (ans, 0, sizeof ans); for (int i = 1; i <= n; i++) { int id = ((i&1)? 1 : -1); //+x还是-x double cur = g[i]+x*id; //当前半径 if (cur >= 0) { ans[i] = cur; } else { //出现了负数 ok = 0; break; } } if (!ok) { printf ("IMPOSSIBLE\n"); return ; } double sum = 0.0; for (int i = 1; i <= n; i++) { sum += ans[i]*ans[i]; } printf ("%.2f\n", sum*pi); for (int i = 1; i <= n; i++) printf ("%.2f\n", ans[i]); return ; } int main () { //freopen ("in", "r", stdin); int t; scanf ("%d", &t); while (t--) { scanf ("%d", &n); for (int i = 1; i <= n; i++) { scanf ("%lf%lf", &p[i].x, &p[i].y); } p[n+1] = p[1]; for (int i = 1; i <= n; i++) { l[i] = dis (p[i], p[i+1]); } g[1] = 0; for (int i = 2; i <= n; i++) { g[i] = l[i-1]-g[i-1]; } if (n&1) solve_odd (); else solve_even (); } return 0; }