JZOJ 5606. 【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.

Solution

  • 考虑先暴力枚举 选几个点点的相对顺序

  • 之后我们要使 Ans=R1R2sin(θ1)+R2R3sin(θ2)++RnR1sin(θn) 最大。

  • 其中 θ 满足条件:θ1+θ2++θn=2π

  • 运用 拉格朗日乘数法,我们可以得出拉格朗日乘子

    λ=R1R2cos(θ1)=R2R3cos(θ2)==RnR1cos(θn)

  • 因为 θi(0,π) (超过了必然不会更优),又 cos(θ) [0,π] 上单调递减。

  • 于是我们可以通过单调性二分出一个最小的 λ 满足 θ1+θ2++θn=2π

  • 接着由 θi=arccos(λRiRi+1) 就可以算出每个 θi

  • 最后统计答案即可,时间复杂度 O(N!NlogAns)

  • 附:拉格朗日乘数法的一般步骤如下(以此题为例):

    1. 设出目标函数 f(R1,,Rn)=RiRi+1sin(θi)

    2. 设出条件函数 g(R1,,Rn)=Ri2π=0

    3. 再设出拉格朗日乘数函数 F(R1,,Rn,λ)=f(R1,,Rn)+λg(R1,,Rn)

    4. 之后就可以求出关于每个元的偏导数(其它元看做常数),联立即可求出各个元:

    5. Fθi=RiRi+1cos(θi)+λ=0Fλ=Ri2π=0

    6. 使各个偏导数都等于 0 ,解出的解就是最值的解了。

Code

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cctype>
using namespace std;
const double Pi=acos(-1.0),eps=1e-7;
int n,a[10];
double t[10],ans;
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
inline double check(double x)
{
    double sum=t[n]=acos(x/a[1]/a[n]);
    for(int i=1;i<n;i++) sum+=t[i]=acos(x/a[i]/a[i+1]);
    return sum;
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    sort(a+1,a+1+n);
    while(n>=3)
    {
        do
        {
            double l=-1.0*a[1]*a[2],r=-l;
            while(l+eps<r)
            {
                double mid=(l+r)/2.0;
                if(check(mid)>=2*Pi) l=mid+eps; else r=mid-eps;
            }
            if(fabs(check(l)-2*Pi)<=eps)
            {
                double num=sin(t[n])*a[1]*a[n];
                for(int i=1;i<n;i++) num+=sin(t[i])*a[i]*a[i+1];
                if(num>ans) ans=num;
            }
        }while(next_permutation(a+1,a+1+n));
        n--;
        for(int i=1;i<=n;i++) a[i]=a[i+1];
    }
    printf("%.7lf",ans/2.0);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值