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
",Ans); 
45 }
原文地址:https://www.cnblogs.com/qzqzgfy/p/5593285.html