BZOJ 2876 骑行川藏
http://www.lydsy.com/JudgeOnline/problem.php?id=2876
拉格朗日乘数法:f'+入g'=0,f为函数的导数,g为限制条件的导数。
思路:E=Σki*si*(vi-vi')^2,贪心可知,当E=Eu时,才能得到最优解。
我们假设函数f=Σsi/vi,限制g=Σki*si*(vi-vi')^2=E
根据拉格朗日乘数法,f'+入g'=0,
g'=2*ki*si*(vi-vi')
f'=-si/(vi^2)
可得-si/(vi^2)+2*入*ki*si*(vi-vi')=0
即2*入*ki*(vi^2)*(vi-vi')=1
由于入与vi负相关,且vi与E正相关,因此入与E负相关,因此满足单调,二分入的值,判断解是否等于E
2*入*ki*(vi^2)*(vi-vi')-1=0中,(vi^2)*(vi-vi')关于vi单调递增(vi>0),也可以通过二分来找方程的根。
1 #include<algorithm> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<iostream> 6 const double eps=1e-12; 7 const double inf=1e9; 8 double ans[10005],k[10005],v[20005],s[20005],E; 9 int n; 10 double calc(int x,double Mid){ 11 double l=std::max(0.0,v[x]),r=inf; 12 while (r-l>eps){ 13 double mid=(l+r)/2; 14 if (mid*mid*2*Mid*k[x]*(mid-v[x])-1>0) r=mid; 15 else l=mid; 16 } 17 return l; 18 } 19 double sqr(double x){ 20 return x*x; 21 } 22 double work(double mid){ 23 for (int i=1;i<=n;i++) 24 ans[i]=calc(i,mid); 25 double res=0; 26 for (int i=1;i<=n;i++) 27 res+=s[i]*k[i]*(sqr(v[i]-ans[i])); 28 return res; 29 } 30 int main(){ 31 scanf("%d",&n);scanf("%lf",&E); 32 for (int i=1;i<=n;i++){ 33 scanf("%lf%lf%lf",&s[i],&k[i],&v[i]); 34 } 35 double l=0,r=1e9; 36 while (r-l>eps){ 37 double mid=(l+r)/2; 38 if (work(mid)>E) l=mid; 39 else r=mid; 40 } 41 double Ans=0; 42 for (int i=1;i<=n;i++) 43 Ans+=s[i]/ans[i]; 44 printf("%.8f\n",Ans); 45 }