某考试 T2 yja

2.1 Description

在平面上找 n 个点, 要求这 n 个点离原点的距离分别为 r1, r2, ..., rn.

最大化这 n 个点构成的凸包面积, 凸包上的点的顺序任意.

2.2 Input Format

第一行一个整数 n.

接下来一行 n 个整数依次表示 ri .

2.3 Output Format

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

2.4 Sample

2.4.1 Input

4

5

8

58

85

2.4.2 Output

2970

2.5 Constraints

对于前 20% 的数据, n ≤ 3;

对于前 40% 的数据, n ≤ 4;

对于另 20% 的数据, r1 = r2 = ... = rn;

对于 100% 的数据, 1 ≤ n ≤ 8, 1 ≤ ri ≤ 1000.

    考试的时候看了一会题就感觉是个拉格朗日乘数法的题,但是我为什么往叉积求面积上想了23333,于是就出来了n个限制然后还得三分套三分套三分.......

于是在想如何优化求偏导都是零的过程中时间过去了2.5h  。。。(我好菜啊我现在退役算了)。

    正解其实就是拉格朗日乘数法,但是求面积是用(1/2) * a * b * sin(a和b的夹角) ,于是这样就只有一个总限制,那就是所有角的和=2*π。

并且利用所有变量的偏导等于0,我们可以得到 λ = r1 * r2 *cos(r1和r2夹角) = .... ,因为最优情况肯定没有超过π的角,而cos在[0,π]上单调,所以我们二分一下λ 就可以得到最后的答案。

    当然因为不一定所有点都在凸包上出现,并且每个点出现的顺序不一定,所以我们还要枚举选ri前几大的点(贪心)和它们的顺序(全排列一下就好了)。

#include<bits/stdc++.h>
#define ll long long
#define D double
using namespace std;
const int maxn=10;
const D pi=acos(-1);
D miu,l,r,thita[maxn];
int n,R[maxn];

inline D calc(){
	D tot=0;
	for(int i=1;i<n;i++) thita[i]=acos(miu/(R[i]*R[i+1])),tot+=thita[i];
	thita[n]=acos(miu/(R[n]*R[1])),tot+=thita[n];
	return tot;
}

inline void solve(){
	D ans=0,now,le,ri;
	
	sort(R+1,R+n+1);
	for(;n>=3;){
		le=-R[1]*R[2],ri=-le;

		while(1){
			l=le,r=ri;
			
			while(r-l>=1e-8){
				miu=(l+r)/2;
				if(calc()>=2*pi) l=miu;
				else r=miu;
			}

			if(fabs(calc()-2*pi)<=1e-6){
			    now=0;
			    for(int i=1;i<n;i++) now+=sin(thita[i])*(D)R[i]*(D)R[i+1];
			    now+=sin(thita[n])*(D)R[n]*(D)R[1];
			    ans=max(ans,now);
		    }
		    
			if(!next_permutation(R+1,R+n+1)) break;
		}
		
		sort(R+1,R+n+1),n--;
		for(int i=1;i<=n;i++) R[i]=R[i+1];
	}
	
	printf("%.11lf
",ans/2);
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",R+i);
	solve();
	return 0;
}

  

 

原文地址:https://www.cnblogs.com/JYYHH/p/8652953.html