[HDU]5531 Rebuild (三分法求下凸函数,维护最小圆面积)

来源:互联网 发布:农家乐商机数据 编辑:程序博客网 时间:2024/06/05 09:07

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5531


题目大意:给定一个凸N边形N个顶点坐标(N>=3),在N边形每个顶点上构造一个圆要求相邻两顶点的圆相切,求能构造出的最小总圆面积

数据范围: 3 <= N <= 10000,  3 <= xi , yi <= 10000

题目解答:三分法求下凸函数(复杂度O(N*log3(N)))

假设第一个点构造的圆半径为r,则其他点构造出的圆的半径可以依次表示出来,最后的答案是关于r的一个二次函数。

对于N边形分奇偶讨论:

  • N为奇数:此时关于r的二次函数没有r的一次项,则最终面积结果可以直接求解
  • N为偶数:此时关于r的二次函数有r的一次项,是一个下凸函数,想到三分求解

注意:三分的时候边界条件处理不好可能会导致WA,注意对最后二分出的结果取(l+r)/ 2作为答案


#include <set>#include <map>#include <stack>#include <queue>#include <deque>#include <cmath>#include <vector>#include <string>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const double pi = acos(-1.0);const double eps = 1e-9;const int maxn = 100010;int n;double dis[maxn], ri[maxn];///dis存边长,ri存半径struct Edge{    double x, y;}e[maxn];int Dcmp(double x){    if(fabs(x) < eps)        return 0;    if(x > 0)        return 1;    return -1;}double Dis(int i, int j){    return sqrt((e[i].x-e[j].x)*(e[i].x-e[j].x) + (e[i].y-e[j].y)*(e[i].y-e[j].y));}double ans;int Check(double r){    double tmp = -r;    for(int i = 0; i < n; ++i)    {        tmp = dis[i] - tmp;        ri[i] = tmp;        if(Dcmp(tmp) < 0)            return 0;    }    ans = 0;    for(int i = 0; i < n; ++i)        ans += ri[i] * ri[i];    return 1;}int main(){    int t, ca = 1;    scanf("%d", &t);    while(t--)    {        scanf("%d", &n);        dis[0] = 0;        for(int i = 0; i < n; ++i)        {            scanf("%lf %lf", &e[i].x, &e[i].y);            if(i)                dis[i] = Dis(i,i-1);            if(i == n-1)                dis[n] = Dis(i,0);        }        double mn = 0, mx = dis[1];        double sum = 0;        for(int i = 1; i <= n; ++i)        {            sum = dis[i] - sum;            if(i & 1)                mx = min(mx,sum);            else                mn = max(mn,-sum);        }        if(Dcmp(mx - mn) < 0)        {            printf("IMPOSSIBLE\n");            continue;        }        if(n & 1)        {            double r = sum / 2;            if(!Check(r) || Dcmp(r) <= 0 )                printf("IMPOSSIBLE\n");            else            {                printf("%.2f\n", ans*pi);                for(int i = 0; i < n; ++i)                    printf("%.2f\n", ri[i]);            }        }        else        {            if(Dcmp(sum) != 0)            {                printf("IMPOSSIBLE\n");                continue;            }            double l = mn, r = mx;            double mid, mmid, s1, s2;            while(l+eps < r)            {                mid = (l+r)/2;                mmid = (mid+r)/2;                Check(mid);                s1 = ans;                Check(mmid);                s2 = ans;                if(s1 <= s2)                    r = mmid;                else                    l = mid;            }            if(Check((l+r)/2))            {                printf("%.2f\n", ans*pi);                for(int i = 0; i < n; ++i)                    printf("%.2f\n", ri[i]);            }            else                printf("IMPOSSIBLE\n");        }    }    return 0;}


0 0