[qzsc2019] 所以SARAZANMAI

题目描述

在平面上,每个人都和原点有一定的距离 $ r_i $ ,人与人之间的联系组成了一个多边形,就是这 $ n $ 个人所组成的凸包。凸包上人的顺序随意。

最好的联系,所对应的多边形面积最大,那么这个多边形的面积是多少?

输入格式

第一行一个整数 $n$ 。

接下来一行 $n$ 个整数依次表示 $ r_i $ 。

输出格式

输出一个实数表示答案, 要求绝对误差或相对误差≤$10^{-6}$

样例

input

4

5

8

58

85

output

2970

Sol

拉格朗日乘数法
考虑在$O(N!)$的时间内枚举点的相对位置
则问题转化为 约束为$sum{x_i}=2π$ 求$sum{r_i*r_{i+1}*sin(xi)}$的极限


构造$ F $ = $  sum{r_i*r_{i+1}*sin(x_i)+k*(sum{x_i}-2π)}$


考虑对于每一个$x_i$求偏导数 并且使F的偏导数为0
则可以发现 是联立形如$r_i*r_{i+1}*cos(xi)+k=0$的形式
即联立多个$r_i*r_{i+1}*cos(xi)=t$的形式
然后将这多个方程与$sum{x_i}=2π$联立
可以发现的是所有的$x_i$关于$t$单调 二分一个$t$ 就可以在$O(n log(max( {ri*r_{i+1} ) }))$时间内求解答案
总复杂度$O(n!*n*log(max{r_i*r_{i+1}}))$

Code

#include <bits/stdc++.h>
using namespace std;
int N,NN;
int a[15];
bool used[1005];
double ans,pi=acos(-1.0);
double r[1005];
const double eps=1e-7;
double Check(double Now){
    double ret=0.0;
    for (int i=1;i<=N;i++)
        ret=ret+acos(Now/r[a[i]]/r[a[i+1]]);
    return ret;    
}
double calc(){
    double l,rr=1e7;
    a[N+1]=a[1];
    for (int i=1;i<=N;i++)
        rr=min(rr,r[a[i]]*r[a[i+1]]);
    l=-rr;    
    while (rr-l>eps){
        double mid=(l+rr)/2.0;
        if (Check(mid)<2.0*pi)
            rr=mid;
        else l=mid;
        }
    if (Check(l)-2.0*pi>eps) return 0;
    double Ans=0.0;
    for (int i=1;i<=N;i++)
        Ans+=r[a[i]]*r[a[i+1]]*sin(acos(l/r[a[i]]/r[a[i+1]]));
    return Ans;
}
void dfs(int Len){
    if (Len>3) {
        a[Len]=a[1];
        N=Len-1;
        ans=max(ans,calc());
    }
    if (Len>NN) return;
    for (int i=1;i<=NN;i++)
        if (!used[i]){
            used[i]=true;
            a[Len]=i;
            dfs(Len+1);
            used[i]=false;
        }
}
int main(){
    scanf("%d",&N);
    NN=N;
    for (int i=1;i<=N;i++)
        cin>>r[i];
    dfs(1);
    printf("%.8lf
",ans/2.0);
    return 0;
}

 

原文地址:https://www.cnblogs.com/si--nian/p/11328406.html