Kvalitetni

link

首先一个重要结论是,由于这个式子中所有参数的取值范围都是任意实数,所以任意一个式子的取值都应该是连续的,也就是说假如一个式子的最大取值为 \(X\),则 \(\forall x\in[0,X]\) 都是可以通过调整一些参数的值得到的,这样一来就好办了,也就是说关于一个人类高质量柿子我们只需要知道它可能的最大值就可以知道它所有可能的取值。

但最大值如何求呢?如果是和的形式那很简单,直接把这个式子中所有部分的最大值加起来,然后和 limit 取较小值即可,因为根据上面的结论,假如和大于了 limit,那么我们可以通过调整式子的参数使得它的值变得小一些然后使得整个大式子的值刚好等于 limit。

那乘的形式怎么办呢?小学奥数说过和定差小积大,也就是 \(a\times b\le(\frac{a+b}{2})^2\) ,且当 \(a=b\) 时可以取到等号。于是可以推而广之得到 \(\prod\limits_{i=1}^Na_i\le(\frac{\sum a_i}{N})^N\),也就是说一个乘积式各项的和确定之后,这些项越接近最后的乘积也就越大,排序之后选择即可。

个人用的递归写法,人傻常熟大,仅供参考。

#include<bits/stdc++.h>
//#define feyn
const int N=1000010;
const int M=100;
using namespace std;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}
inline double min(double s1,double s2){
	return s1<s2?s1:s2;
}

stack<int>st;
int m,len,a[M],pl[N];
char w[N];

inline double f(int l,int r){
	if(l==r)return a[1];//如果括号中只有一个数直接反悔limit 
	int n=0;double data[M];//记录这个区间内所有柿子的最大值 
	char op;
	for(int i=l;i<=r;i++){
		if(w[i]=='(')data[++n]=f(i+1,pl[i]-1),i=pl[i];
		else op=w[i];//记录这些柿子用什么符号连接 
	}
	if(n==1)return data[1];
	if(op=='+'){
		double sum=0;
		for(int i=1;i<=n;i++)sum+=data[i];
		return min(a[n],sum);//如上文,返回和和limit的较小值 
	}
	if(op=='*'){
		double ava,ans=1,sum=a[n];
		sort(data+1,data+n+1);//从小到大贪心选择 
		int pl=1;
		while(pl<=n){
			ava=sum/(n-pl+1);
			if(ava<=data[pl]){
				for(;pl<=n;pl++)ans*=ava;
				break;
			}
			for(;pl<=n;pl++){
				if(data[pl]<=ava)ans*=data[pl],sum-=data[pl];
				else break;
			}
		}
		return ans;//返回即可 
	}
}

signed main(){
	
	#ifdef feyn
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);
	for(int i=1;i<=m;i++)read(a[i]);
	scanf("%s",w+1);len=strlen(w+1);
	for(int i=1;i<=len;i++){
		if(w[i]=='(')st.push(i);
		if(w[i]==')')pl[st.top()]=i,st.pop();
		//预处理出每个左括号对应右括号的位置 
	}
	printf("%f\n",f(1,len));
	
	return 0;
}
posted @ 2022-07-29 08:28  Feyn618  阅读(22)  评论(0编辑  收藏  举报