2015 ACM/ICPC Asia Regional Changchun && HDU - 5531 Rebuild【数学 + 三分】

题意: 给出一系列点,这些点依次相连,最后一个点和第一个点相连,形成一个多边形。问以每个点为圆心画一个圆(半径可为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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值