Description
在平面上找 n 个点, 要求这 n 个点离原点的距离分别为 r 1 ,r 2 ,…,r n . 最大化这 n 个点构成的凸包面积, 凸包上的点的顺序任意.不要求点全部在凸包上
n<=8
Solution
玄学几何题其实并不是
枚举点是否出现和一个顺序,问题变成求
max(12∑i=1nr[i]∗r[imodn+1]∗sin(θi))
限制是:
∑i=1nθi=2π
然后有一个叫拉格朗日乘数的东西,可以做这个式子的极值。
设一个拉格朗日乘数 λ ,极值函数为f(x1,x2..xn),限制函数为g(x1,x2..xn)=c
那么将目标函数改造为F(x1,x2…xn, λ )=f(x1,x2..xn)+ λ (g(x1,x2…xn)-c)
然后对该函数做偏导,每一个变量xi处的偏导为0
这样就可以得到n个方程,足以解出n个变量。
回到这道题,我们可以发现最后偏导出来的结果形如 r[i]∗r[imodn+1]∗cos(θi)+λ=0
对于每一项都成立,并且我们知道 θi 在 [0,π] 范围内是递减的,于是我们可以二分这个 λ ,然后把所有的 θ 求出来。
但是注意有一个细节,你求出来的某个 θ 可能趋于0,这个时候需要舍去这种情况
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef double db;
const db pi=acos(-1);
const db eps=1e-4;
int n,lim,r[10],R[10];
bool vis[10];
db theta[10],ans;
bool check(db x) {
fo(i,1,lim) theta[i]=acos(x*1.0/R[i]/R[i%lim+1]);
db all=0;fo(i,1,lim) all+=theta[i];
return all>2*pi;
}
void dfs(int x) {
if (x>lim) {
int mn=1e6;
fo(i,1,lim) mn=min(mn,R[i]*R[i%lim+1]);
db le=-mn,ri=mn;
while (ri-le>eps) {
db mid=(le+ri)*0.5;
if (check(mid)) le=mid;
else ri=mid;
}
fo(i,1,lim) if (theta[i]<eps) return;
db res=0;
fo(i,1,lim) res+=R[i]*R[i%lim+1]*sin(theta[i]);
ans=max(ans,res/2.0);
return;
}
fo(i,1,n)
if (!vis[i]) {
R[x]=r[i];vis[i]=1;
dfs(x+1);
R[x]=0;vis[i]=0;
}
}
int main() {
freopen("yja.in","r",stdin);
freopen("yja.out","w",stdout);
scanf("%d",&n);
fo(i,1,n) scanf("%d",&r[i]);
fo(i,3,n) {
lim=i;
dfs(1);
}
printf("%.8lf\n",ans);
return 0;
}