HDU 5531 (平面几何 三分)

原创 2015年11月19日 20:56:19

题意是给你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;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

HDU5531 Rebuild 【几何+数学】

Rebuild Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Tota...

hdu 5531 Rebuild(三分)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5531 题目大意: 给出n个点,这n个点可以连成一个凸多边形。现在以多边形的端点作为圆心,分...

HDU 5531 Rebuild 相切的圆们

终于迎来了长春的重现。赛场上太多的遗憾,比赛的时候唯一看都没看一眼的题就是这个题,太可惜了。连同B题一起,为了抢个FB对不住打重现赛的童鞋们了。。。。。求放过,也不知道这个时候写题解算不算违规呢哈哈。...

hdu 2438 Turn the corner(三分)

Turn the corner                                                         Time Limit: 3000/1000 MS (J...
  • caduca
  • caduca
  • 2015年02月04日 16:08
  • 5104

【hdu 5536】【 2015ACM/ICPC亚洲区长春站 】Chip Factory 题意&题解&代码

2015ACM/ICPC亚洲区长春站 字典树
  • DERITt
  • DERITt
  • 2016年04月21日 11:17
  • 1235

推荐一个算法编程学习中文社区-51NOD【算法分级,支持多语言,可在线编译】

最近偶尔发现一个算法编程学习的论坛,刚开始有点好奇,也只是注册了一下。最近有时间好好研究了一下,的确非常赞,所以推荐给大家。功能和介绍看下面介绍吧。首页的标题很给劲,很纯粹的Coding社区。。。。虽...

【HDU5510 2015沈阳赛区B】【KMP or strstr for循环剪枝】Bazinga 循环处思维灵活转化 时间复杂度均摊思想

Bazinga Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Su...

HDU5531-Rebuild(平面几何+数学)

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=5531思路感觉就一数学题,把公式推出来,然后求出最小面积即可,主要是IMPOSSIBLE的判断感觉有点麻烦...
  • Lzedo
  • Lzedo
  • 2016年08月11日 12:11
  • 217

hdu 5531 Rebuild 三分

题目大意:给你一个多边形,每个多边形的顶点都有一个圆,问是否存在一种情况,使得所有相邻的圆相切,切面积和最小。比赛的时想到了三分,但是并没有发现其中的奥义,所以并不能AC!!思路:首先我们可以发现,因...
  • s_h_r
  • s_h_r
  • 2015年11月02日 18:20
  • 285

HDU 5531 Rebuild(三分)——2015ACM/ICPC亚洲区长春站

传送门 RebuildTime Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Tot...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:HDU 5531 (平面几何 三分)
举报原因:
原因补充:

(最多只允许输入30个字)