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!∗N∗logAns) 。
附:拉格朗日乘数法的一般步骤如下(以此题为例):
设出目标函数 f(R1,⋅⋅⋅,Rn)=∑RiRi+1sin(θi)
设出条件函数 g(R1,⋅⋅⋅,Rn)=∑Ri−2π=0
再设出拉格朗日乘数函数 F(R1,⋅⋅⋅,Rn,λ)=f(R1,⋅⋅⋅,Rn)+λg(R1,⋅⋅⋅,Rn) 。
之后就可以求出关于每个元的偏导数(其它元看做常数),联立即可求出各个元:
F′θi=RiRi+1cos(θi)+λ=0,F′λ=∑Ri−2π=0
使各个偏导数都等于 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;
}