JZOJ5606. 【NOI2018模拟3.27】Yja

Description

在平面上找 n 个点, 要求这 n 个点离原点的距离分别为 r 1 ,r 2 ,…,r n . 最大化这 n 个点构成的
凸包面积, 凸包上的点的顺序任意.不要求点全部在凸包上

Input

第一行一个整数 n.
接下来一行 n 个整数依次表示 r i

Output

输出一个实数表示答案, 要求绝对误差或相对误差 ≤ 10^−6 .

Sample Input

4
5
8
58
85

Sample Output

2970

Data Constraint

对于前 20% 的数据, n ≤ 3;
对于前 40% 的数据, n ≤ 4;
对于另 20% 的数据, r 1 = r 2 = … = r n ;
对于 100% 的数据, 1 ≤ n ≤ 8, 1 ≤ r i ≤ 1000.

题解

将凸包分成n个三角形,然后分别统计面积,
一个三角形的面积就是 sinθr1r2/2 s i n θ ∗ r 1 ∗ r 2 / 2
现在要求最优解,
可以得出一个结论,
最优的时候会有
cosθ1r1r2=cosθ2r2r3=cosθ3r3r4... c o s θ 1 ∗ r 1 ∗ r 2 = c o s θ 2 ∗ r 2 ∗ r 3 = c o s θ 3 ∗ r 3 ∗ r 4 . . .
所以,就二分求出这个东西,
然后分别算出所以的θ,
使他们的和为360°
二分的值域就是 [min(riri+1),min(riri+1)] [ − m i n ( r i ∗ r i + 1 ) , − m i n ( r i ∗ r i + 1 ) ]
因为要保证cosθ在[-1,1]中。

code

#include <queue>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <time.h>
#define ll long long
#define N 103
#define M 103
#define db double
#define P putchar
#define G getchar
#define inf 998244353
#define pi 3.1415926535897932384626433832795
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

int n,a[N],mi,m;
db l,r,mid,p[N],sum,ans,R[N];
bool bz[N];

void work()
{
    R[m+1]=R[1];
    mi=2147483647;
    for(int i=1;i<=m;i++)
        mi=min(mi,R[i]*R[i+1]);
    l=-mi;r=mi;

    while(l+0.00001<r)
    {
        mid=(l+r)/2;sum=0;
        for(int i=1;i<=m;i++)
        {
            p[i]=acos(mid/R[i]/R[i+1]),sum+=p[i];
            if(p[i]<0.1)return;
        }
        if(sum>pi*2)l=mid;else r=mid;
    }

    sum=0;
    for(int i=1;i<=m;i++)
        sum=sum+sin(p[i])*R[i]*R[i+1];

    ans=max(ans,sum);
}

void dg(int x)
{
    if(x>m)
    {
        work();
        return;
    }
    for(int i=1;i<=n;i++)
        if(bz[i])
        {
            R[x]=a[i];
            bz[i]=0;
            dg(x+1);
            bz[i]=1;
        }
}

int main()
{
    read(n);mi=2147483647;
    for(int i=1;i<=n;i++)
        read(a[i]);

    for(m=3;m<=n;m++)
    {
        memset(bz,1,sizeof(bz));
        dg(1);
    }

    printf("%lf",ans/2);

    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值